AK+Everywhere: Change URL::path() to serialize_path()

This now defaults to serializing the path with percent decoded segments
(which is what all callers expect), but has an option not to. This fixes
`file://` URLs with spaces in their paths.

The name has been changed to serialize_path() path to make it more clear
that this method will generate a new string each call (except for the
cannot_be_a_base_url() case). A few callers have then been updated to
avoid repeatedly calling this function.
This commit is contained in:
MacDue 2023-04-14 20:12:03 +01:00 committed by Andreas Kling
parent 5acd40c525
commit 35612c6a7f
Notes: sideshowbarker 2024-07-17 05:06:13 +09:00
42 changed files with 131 additions and 123 deletions

View file

@ -27,18 +27,6 @@ URL::URL(StringView string)
} }
} }
DeprecatedString URL::path() const
{
if (cannot_be_a_base_url())
return paths()[0];
StringBuilder builder;
for (auto& path : m_paths) {
builder.append('/');
builder.append(path);
}
return builder.to_deprecated_string();
}
URL URL::complete_url(StringView relative_url) const URL URL::complete_url(StringView relative_url) const
{ {
if (!is_valid()) if (!is_valid())
@ -270,6 +258,18 @@ bool URL::is_special_scheme(StringView scheme)
return scheme.is_one_of("ftp", "file", "http", "https", "ws", "wss"); return scheme.is_one_of("ftp", "file", "http", "https", "ws", "wss");
} }
DeprecatedString URL::serialize_path(ApplyPercentDecoding apply_percent_decoding) const
{
if (cannot_be_a_base_url())
return m_paths[0];
StringBuilder builder;
for (auto& path : m_paths) {
builder.append('/');
builder.append(apply_percent_decoding == ApplyPercentDecoding::Yes ? percent_decode(path) : path);
}
return builder.to_deprecated_string();
}
DeprecatedString URL::serialize_data_url() const DeprecatedString URL::serialize_data_url() const
{ {
VERIFY(m_scheme == "data"); VERIFY(m_scheme == "data");

View file

@ -93,8 +93,7 @@ public:
append_path("", ApplyPercentEncoding::No); append_path("", ApplyPercentEncoding::No);
} }
DeprecatedString path() const; DeprecatedString serialize_path(ApplyPercentDecoding = ApplyPercentDecoding::Yes) const;
DeprecatedString serialize(ExcludeFragment = ExcludeFragment::No) const; DeprecatedString serialize(ExcludeFragment = ExcludeFragment::No) const;
DeprecatedString serialize_for_display() const; DeprecatedString serialize_for_display() const;
DeprecatedString to_deprecated_string() const { return serialize(); } DeprecatedString to_deprecated_string() const { return serialize(); }

View file

@ -24,7 +24,7 @@ TEST_CASE(basic)
EXPECT_EQ(url.scheme(), "http"); EXPECT_EQ(url.scheme(), "http");
EXPECT_EQ(url.host(), "www.serenityos.org"); EXPECT_EQ(url.host(), "www.serenityos.org");
EXPECT_EQ(url.port_or_default(), 80); EXPECT_EQ(url.port_or_default(), 80);
EXPECT_EQ(url.path(), "/"); EXPECT_EQ(url.serialize_path(), "/");
EXPECT(url.query().is_null()); EXPECT(url.query().is_null());
EXPECT(url.fragment().is_null()); EXPECT(url.fragment().is_null());
} }
@ -34,7 +34,7 @@ TEST_CASE(basic)
EXPECT_EQ(url.scheme(), "https"); EXPECT_EQ(url.scheme(), "https");
EXPECT_EQ(url.host(), "www.serenityos.org"); EXPECT_EQ(url.host(), "www.serenityos.org");
EXPECT_EQ(url.port_or_default(), 443); EXPECT_EQ(url.port_or_default(), 443);
EXPECT_EQ(url.path(), "/index.html"); EXPECT_EQ(url.serialize_path(), "/index.html");
EXPECT(url.query().is_null()); EXPECT(url.query().is_null());
EXPECT(url.fragment().is_null()); EXPECT(url.fragment().is_null());
} }
@ -44,7 +44,7 @@ TEST_CASE(basic)
EXPECT_EQ(url.scheme(), "https"); EXPECT_EQ(url.scheme(), "https");
EXPECT_EQ(url.host(), "localhost"); EXPECT_EQ(url.host(), "localhost");
EXPECT_EQ(url.port_or_default(), 1234); EXPECT_EQ(url.port_or_default(), 1234);
EXPECT_EQ(url.path(), "/~anon/test/page.html"); EXPECT_EQ(url.serialize_path(), "/~anon/test/page.html");
EXPECT(url.query().is_null()); EXPECT(url.query().is_null());
EXPECT(url.fragment().is_null()); EXPECT(url.fragment().is_null());
} }
@ -54,7 +54,7 @@ TEST_CASE(basic)
EXPECT_EQ(url.scheme(), "http"); EXPECT_EQ(url.scheme(), "http");
EXPECT_EQ(url.host(), "www.serenityos.org"); EXPECT_EQ(url.host(), "www.serenityos.org");
EXPECT_EQ(url.port_or_default(), 80); EXPECT_EQ(url.port_or_default(), 80);
EXPECT_EQ(url.path(), "/index.html"); EXPECT_EQ(url.serialize_path(), "/index.html");
EXPECT_EQ(url.query(), ""); EXPECT_EQ(url.query(), "");
EXPECT_EQ(url.fragment(), ""); EXPECT_EQ(url.fragment(), "");
} }
@ -64,7 +64,7 @@ TEST_CASE(basic)
EXPECT_EQ(url.scheme(), "http"); EXPECT_EQ(url.scheme(), "http");
EXPECT_EQ(url.host(), "www.serenityos.org"); EXPECT_EQ(url.host(), "www.serenityos.org");
EXPECT_EQ(url.port_or_default(), 80); EXPECT_EQ(url.port_or_default(), 80);
EXPECT_EQ(url.path(), "/index.html"); EXPECT_EQ(url.serialize_path(), "/index.html");
EXPECT_EQ(url.query(), "foo=1&bar=2"); EXPECT_EQ(url.query(), "foo=1&bar=2");
EXPECT(url.fragment().is_null()); EXPECT(url.fragment().is_null());
} }
@ -74,7 +74,7 @@ TEST_CASE(basic)
EXPECT_EQ(url.scheme(), "http"); EXPECT_EQ(url.scheme(), "http");
EXPECT_EQ(url.host(), "www.serenityos.org"); EXPECT_EQ(url.host(), "www.serenityos.org");
EXPECT_EQ(url.port_or_default(), 80); EXPECT_EQ(url.port_or_default(), 80);
EXPECT_EQ(url.path(), "/index.html"); EXPECT_EQ(url.serialize_path(), "/index.html");
EXPECT(url.query().is_null()); EXPECT(url.query().is_null());
EXPECT_EQ(url.fragment(), "fragment"); EXPECT_EQ(url.fragment(), "fragment");
} }
@ -84,7 +84,7 @@ TEST_CASE(basic)
EXPECT_EQ(url.scheme(), "http"); EXPECT_EQ(url.scheme(), "http");
EXPECT_EQ(url.host(), "www.serenityos.org"); EXPECT_EQ(url.host(), "www.serenityos.org");
EXPECT_EQ(url.port_or_default(), 80); EXPECT_EQ(url.port_or_default(), 80);
EXPECT_EQ(url.path(), "/index.html"); EXPECT_EQ(url.serialize_path(), "/index.html");
EXPECT_EQ(url.query(), "foo=1&bar=2&baz=/?"); EXPECT_EQ(url.query(), "foo=1&bar=2&baz=/?");
EXPECT_EQ(url.fragment(), "frag/ment?test#"); EXPECT_EQ(url.fragment(), "frag/ment?test#");
} }
@ -120,7 +120,7 @@ TEST_CASE(file_url_with_hostname)
EXPECT_EQ(url.scheme(), "file"); EXPECT_EQ(url.scheme(), "file");
EXPECT_EQ(url.host(), "courage"); EXPECT_EQ(url.host(), "courage");
EXPECT_EQ(url.port_or_default(), 0); EXPECT_EQ(url.port_or_default(), 0);
EXPECT_EQ(url.path(), "/my/file"); EXPECT_EQ(url.serialize_path(), "/my/file");
EXPECT_EQ(url.serialize(), "file://courage/my/file"); EXPECT_EQ(url.serialize(), "file://courage/my/file");
EXPECT(url.query().is_null()); EXPECT(url.query().is_null());
EXPECT(url.fragment().is_null()); EXPECT(url.fragment().is_null());
@ -132,7 +132,7 @@ TEST_CASE(file_url_with_localhost)
EXPECT(url.is_valid()); EXPECT(url.is_valid());
EXPECT_EQ(url.scheme(), "file"); EXPECT_EQ(url.scheme(), "file");
EXPECT_EQ(url.host(), ""); EXPECT_EQ(url.host(), "");
EXPECT_EQ(url.path(), "/my/file"); EXPECT_EQ(url.serialize_path(), "/my/file");
EXPECT_EQ(url.serialize(), "file:///my/file"); EXPECT_EQ(url.serialize(), "file:///my/file");
} }
@ -142,7 +142,7 @@ TEST_CASE(file_url_without_hostname)
EXPECT(url.is_valid()); EXPECT(url.is_valid());
EXPECT_EQ(url.scheme(), "file"); EXPECT_EQ(url.scheme(), "file");
EXPECT_EQ(url.host(), ""); EXPECT_EQ(url.host(), "");
EXPECT_EQ(url.path(), "/my/file"); EXPECT_EQ(url.serialize_path(), "/my/file");
EXPECT_EQ(url.serialize(), "file:///my/file"); EXPECT_EQ(url.serialize(), "file:///my/file");
} }
@ -151,7 +151,7 @@ TEST_CASE(file_url_with_encoded_characters)
URL url("file:///my/file/test%23file.txt"sv); URL url("file:///my/file/test%23file.txt"sv);
EXPECT(url.is_valid()); EXPECT(url.is_valid());
EXPECT_EQ(url.scheme(), "file"); EXPECT_EQ(url.scheme(), "file");
EXPECT_EQ(url.path(), "/my/file/test%23file.txt"); EXPECT_EQ(url.serialize_path(), "/my/file/test#file.txt");
EXPECT(url.query().is_null()); EXPECT(url.query().is_null());
EXPECT(url.fragment().is_null()); EXPECT(url.fragment().is_null());
} }
@ -161,7 +161,7 @@ TEST_CASE(file_url_with_fragment)
URL url("file:///my/file#fragment"sv); URL url("file:///my/file#fragment"sv);
EXPECT(url.is_valid()); EXPECT(url.is_valid());
EXPECT_EQ(url.scheme(), "file"); EXPECT_EQ(url.scheme(), "file");
EXPECT_EQ(url.path(), "/my/file"); EXPECT_EQ(url.serialize_path(), "/my/file");
EXPECT(url.query().is_null()); EXPECT(url.query().is_null());
EXPECT_EQ(url.fragment(), "fragment"); EXPECT_EQ(url.fragment(), "fragment");
} }
@ -171,7 +171,7 @@ TEST_CASE(file_url_with_root_path)
URL url("file:///"sv); URL url("file:///"sv);
EXPECT(url.is_valid()); EXPECT(url.is_valid());
EXPECT_EQ(url.scheme(), "file"); EXPECT_EQ(url.scheme(), "file");
EXPECT_EQ(url.path(), "/"); EXPECT_EQ(url.serialize_path(), "/");
} }
TEST_CASE(file_url_serialization) TEST_CASE(file_url_serialization)
@ -190,7 +190,7 @@ TEST_CASE(about_url)
EXPECT(url.is_valid()); EXPECT(url.is_valid());
EXPECT_EQ(url.scheme(), "about"); EXPECT_EQ(url.scheme(), "about");
EXPECT(url.host().is_null()); EXPECT(url.host().is_null());
EXPECT_EQ(url.path(), "blank"); EXPECT_EQ(url.serialize_path(), "blank");
EXPECT(url.query().is_null()); EXPECT(url.query().is_null());
EXPECT(url.fragment().is_null()); EXPECT(url.fragment().is_null());
EXPECT_EQ(url.serialize(), "about:blank"); EXPECT_EQ(url.serialize(), "about:blank");
@ -333,7 +333,7 @@ TEST_CASE(create_with_file_scheme)
EXPECT_EQ(url.paths()[0], "home"); EXPECT_EQ(url.paths()[0], "home");
EXPECT_EQ(url.paths()[1], "anon"); EXPECT_EQ(url.paths()[1], "anon");
EXPECT_EQ(url.paths()[2], "README.md"); EXPECT_EQ(url.paths()[2], "README.md");
EXPECT_EQ(url.path(), "/home/anon/README.md"); EXPECT_EQ(url.serialize_path(), "/home/anon/README.md");
EXPECT(url.query().is_null()); EXPECT(url.query().is_null());
EXPECT(url.fragment().is_null()); EXPECT(url.fragment().is_null());
@ -343,10 +343,10 @@ TEST_CASE(create_with_file_scheme)
EXPECT_EQ(url.paths()[0], "home"); EXPECT_EQ(url.paths()[0], "home");
EXPECT_EQ(url.paths()[1], "anon"); EXPECT_EQ(url.paths()[1], "anon");
EXPECT_EQ(url.paths()[2], ""); EXPECT_EQ(url.paths()[2], "");
EXPECT_EQ(url.path(), "/home/anon/"); EXPECT_EQ(url.serialize_path(), "/home/anon/");
url = URL("file:///home/anon/"sv); url = URL("file:///home/anon/"sv);
EXPECT_EQ(url.path(), "/home/anon/"); EXPECT_EQ(url.serialize_path(), "/home/anon/");
} }
TEST_CASE(complete_url) TEST_CASE(complete_url)
@ -356,7 +356,7 @@ TEST_CASE(complete_url)
EXPECT(url.is_valid()); EXPECT(url.is_valid());
EXPECT_EQ(url.scheme(), "http"); EXPECT_EQ(url.scheme(), "http");
EXPECT_EQ(url.host(), "serenityos.org"); EXPECT_EQ(url.host(), "serenityos.org");
EXPECT_EQ(url.path(), "/test.html"); EXPECT_EQ(url.serialize_path(), "/test.html");
EXPECT(url.query().is_null()); EXPECT(url.query().is_null());
EXPECT(url.query().is_null()); EXPECT(url.query().is_null());
EXPECT_EQ(url.cannot_be_a_base_url(), false); EXPECT_EQ(url.cannot_be_a_base_url(), false);
@ -389,7 +389,7 @@ TEST_CASE(unicode)
{ {
URL url { "http://example.com/_ünicöde_téxt_©"sv }; URL url { "http://example.com/_ünicöde_téxt_©"sv };
EXPECT(url.is_valid()); EXPECT(url.is_valid());
EXPECT_EQ(url.path(), "/_%C3%BCnic%C3%B6de_t%C3%A9xt_%C2%A9"); EXPECT_EQ(url.serialize_path(), "/_ünicöde_téxt_©");
EXPECT(url.query().is_null()); EXPECT(url.query().is_null());
EXPECT(url.fragment().is_null()); EXPECT(url.fragment().is_null());
} }
@ -398,14 +398,14 @@ TEST_CASE(complete_file_url_with_base)
{ {
URL url { "file:///home/index.html" }; URL url { "file:///home/index.html" };
EXPECT(url.is_valid()); EXPECT(url.is_valid());
EXPECT_EQ(url.path(), "/home/index.html"); EXPECT_EQ(url.serialize_path(), "/home/index.html");
EXPECT_EQ(url.paths().size(), 2u); EXPECT_EQ(url.paths().size(), 2u);
EXPECT_EQ(url.paths()[0], "home"); EXPECT_EQ(url.paths()[0], "home");
EXPECT_EQ(url.paths()[1], "index.html"); EXPECT_EQ(url.paths()[1], "index.html");
auto sub_url = url.complete_url("js/app.js"sv); auto sub_url = url.complete_url("js/app.js"sv);
EXPECT(sub_url.is_valid()); EXPECT(sub_url.is_valid());
EXPECT_EQ(sub_url.path(), "/home/js/app.js"); EXPECT_EQ(sub_url.serialize_path(), "/home/js/app.js");
} }
TEST_CASE(empty_url_with_base_url) TEST_CASE(empty_url_with_base_url)

View file

@ -146,7 +146,7 @@ void GLContextWidget::drop_event(GUI::DropEvent& event)
if (url.scheme() != "file") if (url.scheme() != "file")
continue; continue;
auto response = FileSystemAccessClient::Client::the().request_file_read_only_approved(window(), url.path()); auto response = FileSystemAccessClient::Client::the().request_file_read_only_approved(window(), url.serialize_path());
if (response.is_error()) if (response.is_error())
return; return;
load_file(response.value().filename(), response.value().release_stream()); load_file(response.value().filename(), response.value().release_stream());

View file

@ -253,7 +253,7 @@ DeprecatedString CookieJar::default_path(const URL& url)
// https://tools.ietf.org/html/rfc6265#section-5.1.4 // https://tools.ietf.org/html/rfc6265#section-5.1.4
// 1. Let uri-path be the path portion of the request-uri if such a portion exists (and empty otherwise). // 1. Let uri-path be the path portion of the request-uri if such a portion exists (and empty otherwise).
DeprecatedString uri_path = url.path(); DeprecatedString uri_path = url.serialize_path();
// 2. If the uri-path is empty or if the first character of the uri-path is not a %x2F ("/") character, output %x2F ("/") and skip the remaining steps. // 2. If the uri-path is empty or if the first character of the uri-path is not a %x2F ("/") character, output %x2F ("/") and skip the remaining steps.
if (uri_path.is_empty() || (uri_path[0] != '/')) if (uri_path.is_empty() || (uri_path[0] != '/'))
@ -376,7 +376,7 @@ Vector<Web::Cookie::Cookie> CookieJar::get_matching_cookies(const URL& url, Depr
return; return;
// The request-uri's path path-matches the cookie's path. // The request-uri's path path-matches the cookie's path.
if (!path_matches(url.path(), cookie.path)) if (!path_matches(url.serialize_path(), cookie.path))
return; return;
// If the cookie's secure-only-flag is true, then the request-uri's scheme must denote a "secure" protocol. // If the cookie's secure-only-flag is true, then the request-uri's scheme must denote a "secure" protocol.

View file

@ -129,13 +129,14 @@ ErrorOr<bool> handle_drop(GUI::DropEvent const& event, DeprecatedString const& d
Vector<DeprecatedString> paths_to_copy; Vector<DeprecatedString> paths_to_copy;
for (auto& url_to_copy : urls) { for (auto& url_to_copy : urls) {
if (!url_to_copy.is_valid() || url_to_copy.path() == target) auto file_path = url_to_copy.serialize_path();
if (!url_to_copy.is_valid() || file_path == target)
continue; continue;
auto new_path = DeprecatedString::formatted("{}/{}", target, LexicalPath::basename(url_to_copy.path())); auto new_path = DeprecatedString::formatted("{}/{}", target, LexicalPath::basename(file_path));
if (url_to_copy.path() == new_path) if (file_path == new_path)
continue; continue;
paths_to_copy.append(url_to_copy.path()); paths_to_copy.append(file_path);
has_accepted_drop = true; has_accepted_drop = true;
} }

View file

@ -202,7 +202,7 @@ void do_paste(DeprecatedString const& target_directory, GUI::Window* window)
dbgln("Cannot paste URI {}", uri_as_string); dbgln("Cannot paste URI {}", uri_as_string);
continue; continue;
} }
source_paths.append(url.path()); source_paths.append(url.serialize_path());
} }
if (!source_paths.is_empty()) { if (!source_paths.is_empty()) {

View file

@ -975,8 +975,9 @@ void MainWidget::drop_event(GUI::DropEvent& event)
if (!request_close()) if (!request_close())
return; return;
if (auto result = open_file(urls.first().path()); result.is_error()) auto file_path = urls.first().serialize_path();
show_error(result.release_error(), "Opening"sv, LexicalPath { urls.first().path() }.basename()); if (auto result = open_file(file_path); result.is_error())
show_error(result.release_error(), "Opening"sv, LexicalPath { file_path }.basename());
} }
} }

View file

@ -98,7 +98,7 @@ MainWidget::MainWidget()
m_web_view = find_descendant_of_type_named<WebView::OutOfProcessWebView>("web_view"); m_web_view = find_descendant_of_type_named<WebView::OutOfProcessWebView>("web_view");
m_web_view->on_link_click = [this](auto& url, auto&, unsigned) { m_web_view->on_link_click = [this](auto& url, auto&, unsigned) {
if (url.scheme() == "file") { if (url.scheme() == "file") {
auto path = LexicalPath { url.path() }; auto path = LexicalPath { url.serialize_path() };
if (!path.is_child_of(Manual::manual_base_path)) { if (!path.is_child_of(Manual::manual_base_path)) {
open_external(url); open_external(url);
return; return;
@ -246,7 +246,7 @@ void MainWidget::open_url(URL const& url)
m_web_view->load(url); m_web_view->load(url);
m_web_view->scroll_to_top(); m_web_view->scroll_to_top();
auto browse_view_index = m_manual_model->index_from_path(url.path()); auto browse_view_index = m_manual_model->index_from_path(url.serialize_path());
if (browse_view_index.has_value()) { if (browse_view_index.has_value()) {
if (browse_view_index.value() != m_browse_view->selection_start_index()) { if (browse_view_index.value() != m_browse_view->selection_start_index()) {
m_browse_view->expand_all_parents_of(browse_view_index.value()); m_browse_view->expand_all_parents_of(browse_view_index.value());

View file

@ -604,7 +604,7 @@ void HexEditorWidget::drop_event(GUI::DropEvent& event)
return; return;
// TODO: A drop event should be considered user consent for opening a file // TODO: A drop event should be considered user consent for opening a file
auto response = FileSystemAccessClient::Client::the().request_file(window(), urls.first().path(), Core::File::OpenMode::Read); auto response = FileSystemAccessClient::Client::the().request_file(window(), urls.first().serialize_path(), Core::File::OpenMode::Read);
if (response.is_error()) if (response.is_error())
return; return;
open_file(response.value().filename(), response.value().release_stream()); open_file(response.value().filename(), response.value().release_stream());

View file

@ -100,7 +100,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
window->move_to_front(); window->move_to_front();
auto path = urls.first().path(); auto path = urls.first().serialize_path();
auto result = FileSystemAccessClient::Client::the().request_file_read_only_approved(window, path); auto result = FileSystemAccessClient::Client::the().request_file_read_only_approved(window, path);
if (result.is_error()) if (result.is_error())
return; return;
@ -109,7 +109,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
widget->open_file(value.filename(), value.stream()); widget->open_file(value.filename(), value.stream());
for (size_t i = 1; i < urls.size(); ++i) { for (size_t i = 1; i < urls.size(); ++i) {
Desktop::Launcher::open(URL::create_with_file_scheme(urls[i].path().characters()), "/bin/ImageViewer"); Desktop::Launcher::open(URL::create_with_file_scheme(urls[i].serialize_path().characters()), "/bin/ImageViewer");
} }
}; };
widget->on_doubleclick = [&] { widget->on_doubleclick = [&] {

View file

@ -1401,7 +1401,7 @@ void MainWidget::drop_event(GUI::DropEvent& event)
if (url.scheme() != "file") if (url.scheme() != "file")
continue; continue;
auto response = FileSystemAccessClient::Client::the().request_file(window(), url.path(), Core::File::OpenMode::Read); auto response = FileSystemAccessClient::Client::the().request_file(window(), url.serialize_path(), Core::File::OpenMode::Read);
if (response.is_error()) if (response.is_error())
return; return;
open_image(response.release_value()); open_image(response.release_value());

View file

@ -214,6 +214,6 @@ void PresenterWidget::drop_event(GUI::DropEvent& event)
return; return;
window()->move_to_front(); window()->move_to_front();
set_file(urls.first().path()); set_file(urls.first().serialize_path());
} }
} }

View file

@ -141,9 +141,10 @@ bool RunWindow::run_via_launch(DeprecatedString const& run_input)
auto url = URL::create_with_url_or_path(run_input); auto url = URL::create_with_url_or_path(run_input);
if (url.scheme() == "file") { if (url.scheme() == "file") {
auto real_path_or_error = FileSystem::real_path(url.path()); auto file_path = url.serialize_path();
auto real_path_or_error = FileSystem::real_path(file_path);
if (real_path_or_error.is_error()) { if (real_path_or_error.is_error()) {
warnln("Failed to launch '{}': {}", url.path(), real_path_or_error.error()); warnln("Failed to launch '{}': {}", file_path, real_path_or_error.error());
return false; return false;
} }
url = URL::create_with_url_or_path(real_path_or_error.release_value().to_deprecated_string()); url = URL::create_with_url_or_path(real_path_or_error.release_value().to_deprecated_string());

View file

@ -161,7 +161,7 @@ void SoundPlayerWidgetAdvancedView::drop_event(GUI::DropEvent& event)
return; return;
window()->move_to_front(); window()->move_to_front();
// FIXME: Add all paths from drop event to the playlist // FIXME: Add all paths from drop event to the playlist
play_file_path(urls.first().path()); play_file_path(urls.first().serialize_path());
} }
} }

View file

@ -84,24 +84,25 @@ HelpWindow::HelpWindow(GUI::Window* parent)
m_webview->on_link_click = [this](auto& url, auto&, auto&&) { m_webview->on_link_click = [this](auto& url, auto&, auto&&) {
VERIFY(url.scheme() == "spreadsheet"); VERIFY(url.scheme() == "spreadsheet");
if (url.host() == "example") { if (url.host() == "example") {
auto entry = LexicalPath::basename(url.path()); auto example_path = url.serialize_path();
auto entry = LexicalPath::basename(example_path);
auto doc_option = m_docs.get_object(entry); auto doc_option = m_docs.get_object(entry);
if (!doc_option.has_value()) { if (!doc_option.has_value()) {
GUI::MessageBox::show_error(this, DeprecatedString::formatted("No documentation entry found for '{}'", url.path())); GUI::MessageBox::show_error(this, DeprecatedString::formatted("No documentation entry found for '{}'", example_path));
return; return;
} }
auto& doc = doc_option.value(); auto& doc = doc_option.value();
const auto& name = url.fragment(); auto name = url.fragment();
auto maybe_example_data = doc.get_object("example_data"sv); auto maybe_example_data = doc.get_object("example_data"sv);
if (!maybe_example_data.has_value()) { if (!maybe_example_data.has_value()) {
GUI::MessageBox::show_error(this, DeprecatedString::formatted("No example data found for '{}'", url.path())); GUI::MessageBox::show_error(this, DeprecatedString::formatted("No example data found for '{}'", example_path));
return; return;
} }
auto& example_data = maybe_example_data.value(); auto& example_data = maybe_example_data.value();
if (!example_data.has_object(name)) { if (!example_data.has_object(name)) {
GUI::MessageBox::show_error(this, DeprecatedString::formatted("Example '{}' not found for '{}'", name, url.path())); GUI::MessageBox::show_error(this, DeprecatedString::formatted("Example '{}' not found for '{}'", name, example_path));
return; return;
} }
auto& value = example_data.get_object(name).value(); auto& value = example_data.get_object(name).value();
@ -115,14 +116,14 @@ HelpWindow::HelpWindow(GUI::Window* parent)
auto widget = window->set_main_widget<SpreadsheetWidget>(window, Vector<NonnullRefPtr<Sheet>> {}, false).release_value_but_fixme_should_propagate_errors(); auto widget = window->set_main_widget<SpreadsheetWidget>(window, Vector<NonnullRefPtr<Sheet>> {}, false).release_value_but_fixme_should_propagate_errors();
auto sheet = Sheet::from_json(value, widget->workbook()); auto sheet = Sheet::from_json(value, widget->workbook());
if (!sheet) { if (!sheet) {
GUI::MessageBox::show_error(this, DeprecatedString::formatted("Corrupted example '{}' in '{}'", name, url.path())); GUI::MessageBox::show_error(this, DeprecatedString::formatted("Corrupted example '{}' in '{}'", name, example_path));
return; return;
} }
widget->add_sheet(sheet.release_nonnull()); widget->add_sheet(sheet.release_nonnull());
window->show(); window->show();
} else if (url.host() == "doc") { } else if (url.host() == "doc") {
auto entry = LexicalPath::basename(url.path()); auto entry = LexicalPath::basename(url.serialize_path());
m_webview->load(URL::create_with_data("text/html", render(entry))); m_webview->load(URL::create_with_data("text/html", render(entry)));
} else { } else {
dbgln("Invalid spreadsheet action domain '{}'", url.host()); dbgln("Invalid spreadsheet action domain '{}'", url.host());

View file

@ -270,7 +270,7 @@ Optional<Position> Sheet::position_from_url(const URL& url) const
} }
// FIXME: Figure out a way to do this cross-process. // FIXME: Figure out a way to do this cross-process.
VERIFY(url.path() == DeprecatedString::formatted("/{}", getpid())); VERIFY(url.serialize_path() == DeprecatedString::formatted("/{}", getpid()));
return parse_cell_name(url.fragment()); return parse_cell_name(url.fragment());
} }

View file

@ -845,7 +845,7 @@ void MainWidget::drop_event(GUI::DropEvent& event)
if (!request_close()) if (!request_close())
return; return;
auto response = FileSystemAccessClient::Client::the().request_file_read_only_approved(window(), urls.first().path()); auto response = FileSystemAccessClient::Client::the().request_file_read_only_approved(window(), urls.first().serialize_path());
if (response.is_error()) if (response.is_error())
return; return;
if (auto result = read_file(response.value().filename(), response.value().stream()); result.is_error()) if (auto result = read_file(response.value().filename(), response.value().stream()); result.is_error())

View file

@ -671,7 +671,7 @@ void MainWidget::drop_event(GUI::DropEvent& event)
if (request_close() == GUI::Window::CloseRequestDecision::StayOpen) if (request_close() == GUI::Window::CloseRequestDecision::StayOpen)
return; return;
auto response = FileSystemAccessClient::Client::the().request_file(window(), urls.first().path(), Core::File::OpenMode::Read); auto response = FileSystemAccessClient::Client::the().request_file(window(), urls.first().serialize_path(), Core::File::OpenMode::Read);
if (response.is_error()) if (response.is_error())
return; return;

View file

@ -279,7 +279,7 @@ void VideoPlayerWidget::drop_event(GUI::DropEvent& event)
GUI::MessageBox::show_error(window(), "VideoPlayer can only view one clip at a time!"sv); GUI::MessageBox::show_error(window(), "VideoPlayer can only view one clip at a time!"sv);
return; return;
} }
auto response = FileSystemAccessClient::Client::the().request_file_read_only_approved(window(), urls.first().path()); auto response = FileSystemAccessClient::Client::the().request_file_read_only_approved(window(), urls.first().serialize_path());
if (response.is_error()) if (response.is_error())
return; return;
open_file(response.value().filename()); open_file(response.value().filename());

View file

@ -384,7 +384,7 @@ void Editor::drop_event(GUI::DropEvent& event)
return; return;
} }
set_current_editor_wrapper(static_cast<EditorWrapper*>(parent())); set_current_editor_wrapper(static_cast<EditorWrapper*>(parent()));
open_file(urls.first().path()); open_file(urls.first().serialize_path());
} }
} }

View file

@ -480,7 +480,7 @@ void MainWidget::drop_event(GUI::DropEvent& drop_event)
if (!scheme.equals_ignoring_ascii_case("file"sv)) if (!scheme.equals_ignoring_ascii_case("file"sv))
continue; continue;
auto lexical_path = LexicalPath(url.path()); auto lexical_path = LexicalPath(url.serialize_path());
open_script_from_file(lexical_path); open_script_from_file(lexical_path);
} }
} }

View file

@ -18,7 +18,7 @@ DeprecatedString Document::render_to_html() const
StringBuilder html_builder; StringBuilder html_builder;
html_builder.append("<!DOCTYPE html>\n<html>\n"sv); html_builder.append("<!DOCTYPE html>\n<html>\n"sv);
html_builder.append("<head>\n<title>"sv); html_builder.append("<head>\n<title>"sv);
html_builder.append(m_url.path()); html_builder.append(m_url.serialize_path());
html_builder.append("</title>\n</head>\n"sv); html_builder.append("</title>\n</head>\n"sv);
html_builder.append("<body>\n"sv); html_builder.append("<body>\n"sv);
for (auto& line : m_lines) { for (auto& line : m_lines) {

View file

@ -49,9 +49,9 @@ ErrorOr<ByteBuffer> HttpRequest::to_raw_request() const
TRY(builder.try_append(method_name())); TRY(builder.try_append(method_name()));
TRY(builder.try_append(' ')); TRY(builder.try_append(' '));
// NOTE: The percent_encode is so that e.g. spaces are properly encoded. // NOTE: The percent_encode is so that e.g. spaces are properly encoded.
auto path = m_url.path(); auto path = m_url.serialize_path();
VERIFY(!path.is_empty()); VERIFY(!path.is_empty());
TRY(builder.try_append(URL::percent_encode(m_url.path(), URL::PercentEncodeSet::EncodeURI))); TRY(builder.try_append(URL::percent_encode(path, URL::PercentEncodeSet::EncodeURI)));
if (!m_url.query().is_empty()) { if (!m_url.query().is_empty()) {
TRY(builder.try_append('?')); TRY(builder.try_append('?'));
TRY(builder.try_append(m_url.query())); TRY(builder.try_append(m_url.query()));

View file

@ -867,7 +867,7 @@ void TerminalWidget::mousemove_event(GUI::MouseEvent& event)
auto handlers = Desktop::Launcher::get_handlers_for_url(attribute.href); auto handlers = Desktop::Launcher::get_handlers_for_url(attribute.href);
if (!handlers.is_empty()) { if (!handlers.is_empty()) {
auto url = URL(attribute.href); auto url = URL(attribute.href);
auto path = url.path(); auto path = url.serialize_path();
auto app_file = Desktop::AppFile::get_for_app(LexicalPath::basename(handlers[0])); auto app_file = Desktop::AppFile::get_for_app(LexicalPath::basename(handlers[0]));
auto app_name = app_file->is_valid() ? app_file->name() : LexicalPath::basename(handlers[0]); auto app_name = app_file->is_valid() ? app_file->name() : LexicalPath::basename(handlers[0]);
@ -1146,7 +1146,7 @@ void TerminalWidget::context_menu_event(GUI::ContextMenuEvent& event)
})); }));
m_context_menu_for_hyperlink->add_action(GUI::Action::create("Copy &Name", [&](auto&) { m_context_menu_for_hyperlink->add_action(GUI::Action::create("Copy &Name", [&](auto&) {
// file://courage/home/anon/something -> /home/anon/something // file://courage/home/anon/something -> /home/anon/something
auto path = URL(m_context_menu_href).path(); auto path = URL(m_context_menu_href).serialize_path();
// /home/anon/something -> something // /home/anon/something -> something
auto name = LexicalPath::basename(path); auto name = LexicalPath::basename(path);
GUI::Clipboard::the().set_plain_text(name); GUI::Clipboard::the().set_plain_text(name);
@ -1177,7 +1177,7 @@ void TerminalWidget::drop_event(GUI::DropEvent& event)
send_non_user_input(" "sv.bytes()); send_non_user_input(" "sv.bytes());
if (url.scheme() == "file") if (url.scheme() == "file")
send_non_user_input(url.path().bytes()); send_non_user_input(url.serialize_path().bytes());
else else
send_non_user_input(url.to_deprecated_string().bytes()); send_non_user_input(url.to_deprecated_string().bytes());

View file

@ -680,7 +680,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<PendingResponse>> scheme_fetch(JS::Realm& r
// a body. // a body.
// NOTE: URLs such as "about:config" are handled during navigation and result in a network error in the context // NOTE: URLs such as "about:config" are handled during navigation and result in a network error in the context
// of fetching. // of fetching.
if (request->current_url().path() == "blank"sv) { if (request->current_url().serialize_path() == "blank"sv) {
auto response = Infrastructure::Response::create(vm); auto response = Infrastructure::Response::create(vm);
response->set_status_message(MUST(ByteBuffer::copy("OK"sv.bytes()))); response->set_status_message(MUST(ByteBuffer::copy("OK"sv.bytes())));
auto header = MUST(Infrastructure::Header::from_string_pair("Content-Type"sv, "text/html;charset=utf-8"sv)); auto header = MUST(Infrastructure::Header::from_string_pair("Content-Type"sv, "text/html;charset=utf-8"sv));

View file

@ -310,7 +310,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<Request>> Request::construct_impl(JS::Realm
// - parsedReferrers origin is not same origin with origin // - parsedReferrers origin is not same origin with origin
// then set requests referrer to "client". // then set requests referrer to "client".
// FIXME: Actually use the given origin once we have https://url.spec.whatwg.org/#concept-url-origin. // FIXME: Actually use the given origin once we have https://url.spec.whatwg.org/#concept-url-origin.
if ((parsed_referrer.scheme() == "about"sv && parsed_referrer.path() == "client"sv) || !HTML::Origin().is_same_origin(origin)) { if ((parsed_referrer.scheme() == "about"sv && parsed_referrer.serialize_path() == "client"sv) || !HTML::Origin().is_same_origin(origin)) {
request->set_referrer(Infrastructure::Request::Referrer::Client); request->set_referrer(Infrastructure::Request::Referrer::Client);
} }
// 4. Otherwise, set requests referrer to parsedReferrer. // 4. Otherwise, set requests referrer to parsedReferrer.

View file

@ -39,7 +39,7 @@ static bool url_matches_about_blank(AK::URL const& url)
{ {
// A URL matches about:blank if its scheme is "about", its path contains a single string "blank", its username and password are the empty string, and its host is null. // A URL matches about:blank if its scheme is "about", its path contains a single string "blank", its username and password are the empty string, and its host is null.
return url.scheme() == "about"sv return url.scheme() == "about"sv
&& url.path() == "blank"sv && url.serialize_path() == "blank"sv
&& url.username().is_empty() && url.username().is_empty()
&& url.password().is_empty() && url.password().is_empty()
&& url.host().is_null(); && url.host().is_null();

View file

@ -121,7 +121,7 @@ static bool url_matches_about_blank(AK::URL const& url)
{ {
// A URL matches about:blank if its scheme is "about", its path contains a single string "blank", its username and password are the empty string, and its host is null. // A URL matches about:blank if its scheme is "about", its path contains a single string "blank", its username and password are the empty string, and its host is null.
return url.scheme() == "about"sv return url.scheme() == "about"sv
&& url.path() == "blank"sv && url.serialize_path() == "blank"sv
&& url.username().is_empty() && url.username().is_empty()
&& url.password().is_empty() && url.password().is_empty()
&& url.host().is_null(); && url.host().is_null();

View file

@ -293,7 +293,7 @@ DeprecatedString HTMLHyperlinkElementUtils::pathname() const
// 4. If url's cannot-be-a-base-URL is true, then return url's path[0]. // 4. If url's cannot-be-a-base-URL is true, then return url's path[0].
// 5. If url's path is empty, then return the empty string. // 5. If url's path is empty, then return the empty string.
// 6. Return "/", followed by the strings in url's path (including empty strings), separated from each other by "/". // 6. Return "/", followed by the strings in url's path (including empty strings), separated from each other by "/".
return m_url->path(); return m_url->serialize_path();
} }
// https://html.spec.whatwg.org/multipage/links.html#dom-hyperlink-pathname // https://html.spec.whatwg.org/multipage/links.html#dom-hyperlink-pathname

View file

@ -233,7 +233,7 @@ WebIDL::ExceptionOr<String> Location::pathname() const
return WebIDL::SecurityError::create(realm(), "Location's relevant document is not same origin-domain with the entry settings object's origin"sv); return WebIDL::SecurityError::create(realm(), "Location's relevant document is not same origin-domain with the entry settings object's origin"sv);
// 2. Return the result of URL path serializing this Location object's url. // 2. Return the result of URL path serializing this Location object's url.
return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(url().path())); return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(url().serialize_path()));
} }
WebIDL::ExceptionOr<void> Location::set_pathname(String const&) WebIDL::ExceptionOr<void> Location::set_pathname(String const&)

View file

@ -92,7 +92,7 @@ WebIDL::ExceptionOr<String> WorkerLocation::pathname() const
{ {
auto& vm = realm().vm(); auto& vm = realm().vm();
// The pathname getter steps are to return the result of URL path serializing this's WorkerGlobalScope object's url. // The pathname getter steps are to return the result of URL path serializing this's WorkerGlobalScope object's url.
return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(m_global_scope->url().path())); return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(m_global_scope->url().serialize_path()));
} }
// https://html.spec.whatwg.org/multipage/workers.html#dom-workerlocation-search // https://html.spec.whatwg.org/multipage/workers.html#dom-workerlocation-search

View file

@ -144,7 +144,7 @@ static bool build_image_document(DOM::Document& document, ByteBuffer const& data
auto title_element = DOM::create_element(document, HTML::TagNames::title, Namespace::HTML).release_value_but_fixme_should_propagate_errors(); auto title_element = DOM::create_element(document, HTML::TagNames::title, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
MUST(head_element->append_child(title_element)); MUST(head_element->append_child(title_element));
auto basename = LexicalPath::basename(document.url().path()); auto basename = LexicalPath::basename(document.url().serialize_path());
auto title_text = document.heap().allocate<DOM::Text>(document.realm(), document, DeprecatedString::formatted("{} [{}x{}]", basename, bitmap->width(), bitmap->height())).release_allocated_value_but_fixme_should_propagate_errors(); auto title_text = document.heap().allocate<DOM::Text>(document.realm(), document, DeprecatedString::formatted("{} [{}x{}]", basename, bitmap->width(), bitmap->height())).release_allocated_value_but_fixme_should_propagate_errors();
MUST(title_element->append_child(*title_text)); MUST(title_element->append_child(*title_text));

View file

@ -103,7 +103,7 @@ void Resource::did_load(Badge<ResourceLoader>, ReadonlyBytes data, HashMap<Depre
// FIXME: "The Quite OK Image Format" doesn't have an official mime type yet, // FIXME: "The Quite OK Image Format" doesn't have an official mime type yet,
// and servers like nginx will send a generic octet-stream mime type instead. // and servers like nginx will send a generic octet-stream mime type instead.
// Let's use image/x-qoi for now, which is also what our Core::MimeData uses & would guess. // Let's use image/x-qoi for now, which is also what our Core::MimeData uses & would guess.
if (m_mime_type == "application/octet-stream" && url().path().ends_with(".qoi"sv)) if (m_mime_type == "application/octet-stream" && url().serialize_path().ends_with(".qoi"sv))
m_mime_type = "image/x-qoi"; m_mime_type = "image/x-qoi";
} else if (url().scheme() == "data" && !url().data_mime_type().is_empty()) { } else if (url().scheme() == "data" && !url().data_mime_type().is_empty()) {
dbgln_if(RESOURCE_DEBUG, "This is a data URL with mime-type _{}_", url().data_mime_type()); dbgln_if(RESOURCE_DEBUG, "This is a data URL with mime-type _{}_", url().data_mime_type());
@ -113,7 +113,7 @@ void Resource::did_load(Badge<ResourceLoader>, ReadonlyBytes data, HashMap<Depre
if (content_type_options.value_or("").equals_ignoring_ascii_case("nosniff"sv)) { if (content_type_options.value_or("").equals_ignoring_ascii_case("nosniff"sv)) {
m_mime_type = "text/plain"; m_mime_type = "text/plain";
} else { } else {
m_mime_type = Core::guess_mime_type_based_on_filename(url().path()); m_mime_type = Core::guess_mime_type_based_on_filename(url().serialize_path());
} }
} }

View file

@ -237,7 +237,7 @@ void ResourceLoader::load(LoadRequest& request, Function<void(ReadonlyBytes, Has
if (!m_page.has_value()) if (!m_page.has_value())
return; return;
FileRequest file_request(url.path(), [this, success_callback = move(success_callback), error_callback = move(error_callback), log_success, log_failure, request](ErrorOr<i32> file_or_error) { FileRequest file_request(url.serialize_path(), [this, success_callback = move(success_callback), error_callback = move(error_callback), log_success, log_failure, request](ErrorOr<i32> file_or_error) {
--m_pending_loads; --m_pending_loads;
if (on_load_counter_change) if (on_load_counter_change)
on_load_counter_change(); on_load_counter_change();
@ -275,7 +275,7 @@ void ResourceLoader::load(LoadRequest& request, Function<void(ReadonlyBytes, Has
// NOTE: For file:// URLs, we have to guess the MIME type, since there's no HTTP header to tell us what this is. // NOTE: For file:// URLs, we have to guess the MIME type, since there's no HTTP header to tell us what this is.
// We insert a fake Content-Type header here, so that clients can use it to learn the MIME type. // We insert a fake Content-Type header here, so that clients can use it to learn the MIME type.
HashMap<DeprecatedString, DeprecatedString, CaseInsensitiveStringTraits> response_headers; HashMap<DeprecatedString, DeprecatedString, CaseInsensitiveStringTraits> response_headers;
auto mime_type = Core::guess_mime_type_based_on_filename(request.url().path()); auto mime_type = Core::guess_mime_type_based_on_filename(request.url().serialize_path());
response_headers.set("Content-Type"sv, mime_type); response_headers.set("Content-Type"sv, mime_type);
success_callback(data, response_headers, {}); success_callback(data, response_headers, {});

View file

@ -323,7 +323,7 @@ WebIDL::ExceptionOr<String> URL::pathname() const
auto& vm = realm().vm(); auto& vm = realm().vm();
// The pathname getter steps are to return the result of URL path serializing thiss URL. // The pathname getter steps are to return the result of URL path serializing thiss URL.
return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(m_url.path())); return TRY_OR_THROW_OOM(vm, String::from_deprecated_string(m_url.serialize_path()));
} }
// https://url.spec.whatwg.org/#ref-for-dom-url-pathname%E2%91%A0 // https://url.spec.whatwg.org/#ref-for-dom-url-pathname%E2%91%A0

View file

@ -26,10 +26,11 @@ DeprecatedString ConnectionInfo::resource_name() const
// The "resource-name" can be constructed by concatenating the following: // The "resource-name" can be constructed by concatenating the following:
StringBuilder builder; StringBuilder builder;
// "/" if the path component is empty // "/" if the path component is empty
if (m_url.path().is_empty()) auto path = m_url.serialize_path();
if (path.is_empty())
builder.append('/'); builder.append('/');
// The path component // The path component
builder.append(m_url.path()); builder.append(path);
// "?" if the query component is non-empty // "?" if the query component is non-empty
if (!m_url.query().is_empty()) if (!m_url.query().is_empty())
builder.append('?'); builder.append('?');

View file

@ -141,7 +141,7 @@ Vector<DeprecatedString> Launcher::handlers_for_url(const URL& url)
{ {
Vector<DeprecatedString> handlers; Vector<DeprecatedString> handlers;
if (url.scheme() == "file") { if (url.scheme() == "file") {
for_each_handler_for_path(url.path(), [&](auto& handler) -> bool { for_each_handler_for_path(url.serialize_path(), [&](auto& handler) -> bool {
handlers.append(handler.executable); handlers.append(handler.executable);
return true; return true;
}); });
@ -161,7 +161,7 @@ Vector<DeprecatedString> Launcher::handlers_with_details_for_url(const URL& url)
{ {
Vector<DeprecatedString> handlers; Vector<DeprecatedString> handlers;
if (url.scheme() == "file") { if (url.scheme() == "file") {
for_each_handler_for_path(url.path(), [&](auto& handler) -> bool { for_each_handler_for_path(url.serialize_path(), [&](auto& handler) -> bool {
handlers.append(handler.to_details_str()); handlers.append(handler.to_details_str());
return true; return true;
}); });
@ -211,7 +211,7 @@ bool Launcher::open_with_handler_name(const URL& url, DeprecatedString const& ha
auto& handler = handler_optional.value(); auto& handler = handler_optional.value();
DeprecatedString argument; DeprecatedString argument;
if (url.scheme() == "file") if (url.scheme() == "file")
argument = url.path(); argument = url.serialize_path();
else else
argument = url.to_deprecated_string(); argument = url.to_deprecated_string();
return spawn(handler.executable, { argument }); return spawn(handler.executable, { argument });
@ -348,7 +348,8 @@ void Launcher::for_each_handler_for_path(DeprecatedString const& path, Function<
bool Launcher::open_file_url(const URL& url) bool Launcher::open_file_url(const URL& url)
{ {
struct stat st; struct stat st;
if (stat(url.path().characters(), &st) < 0) { auto file_path = url.serialize_path();
if (stat(file_path.characters(), &st) < 0) {
perror("stat"); perror("stat");
return false; return false;
} }
@ -356,11 +357,11 @@ bool Launcher::open_file_url(const URL& url)
if (S_ISDIR(st.st_mode)) { if (S_ISDIR(st.st_mode)) {
Vector<DeprecatedString> fm_arguments; Vector<DeprecatedString> fm_arguments;
if (url.fragment().is_empty()) { if (url.fragment().is_empty()) {
fm_arguments.append(url.path()); fm_arguments.append(file_path);
} else { } else {
fm_arguments.append("-s"); fm_arguments.append("-s");
fm_arguments.append("-r"); fm_arguments.append("-r");
fm_arguments.append(DeprecatedString::formatted("{}/{}", url.path(), url.fragment())); fm_arguments.append(DeprecatedString::formatted("{}/{}", file_path, url.fragment()));
} }
auto handler_optional = m_file_handlers.get("directory"); auto handler_optional = m_file_handlers.get("directory");
@ -372,10 +373,10 @@ bool Launcher::open_file_url(const URL& url)
} }
if ((st.st_mode & S_IFMT) == S_IFREG && st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) if ((st.st_mode & S_IFMT) == S_IFREG && st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
return spawn(url.path(), {}); return spawn(file_path, {});
auto extension = LexicalPath::extension(url.path()).to_lowercase(); auto extension = LexicalPath::extension(file_path).to_lowercase();
auto mime_type = mime_type_for_file(url.path()); auto mime_type = mime_type_for_file(file_path);
auto mime_type_or_extension = extension; auto mime_type_or_extension = extension;
bool should_use_mime_type = mime_type.has_value() && has_mime_handlers(mime_type.value()); bool should_use_mime_type = mime_type.has_value() && has_mime_handlers(mime_type.value());
@ -389,7 +390,7 @@ bool Launcher::open_file_url(const URL& url)
// Additional parameters parsing, specific for the file protocol and txt file handlers // Additional parameters parsing, specific for the file protocol and txt file handlers
Vector<DeprecatedString> additional_parameters; Vector<DeprecatedString> additional_parameters;
DeprecatedString filepath = url.path(); DeprecatedString filepath = url.serialize_path();
auto parameters = url.query().split('&'); auto parameters = url.query().split('&');
for (auto const& parameter : parameters) { for (auto const& parameter : parameters) {

View file

@ -243,13 +243,14 @@ void QuickLaunchWidget::drop_event(GUI::DropEvent& event)
if (event.mime_data().has_urls()) { if (event.mime_data().has_urls()) {
auto urls = event.mime_data().urls(); auto urls = event.mime_data().urls();
for (auto& url : urls) { for (auto& url : urls) {
auto entry = QuickLaunchEntry::create_from_path(url.path()); auto path = url.serialize_path();
auto entry = QuickLaunchEntry::create_from_path(path);
if (entry) { if (entry) {
auto item_name = sanitize_entry_name(entry->name()); auto item_name = sanitize_entry_name(entry->name());
auto result = add_or_adjust_button(item_name, entry.release_nonnull()); auto result = add_or_adjust_button(item_name, entry.release_nonnull());
if (result.is_error()) if (result.is_error())
GUI::MessageBox::show_error(window(), DeprecatedString::formatted("Failed to add quick launch entry: {}", result.release_error())); GUI::MessageBox::show_error(window(), DeprecatedString::formatted("Failed to add quick launch entry: {}", result.release_error()));
Config::write_string("Taskbar"sv, quick_launch, item_name, url.path()); Config::write_string("Taskbar"sv, quick_launch, item_name, path);
} }
} }
} }

View file

@ -192,29 +192,30 @@ RecursionDecision MarkdownLinkage::visit(Markdown::Text::LinkNode const& link_no
m_has_invalid_link = true; m_has_invalid_link = true;
return RecursionDecision::Recurse; return RecursionDecision::Recurse;
} }
if (url.paths().size() < 2) { if (url.path_segment_count() < 2) {
warnln("help://man URL is missing section or page: {}", href); warnln("help://man URL is missing section or page: {}", href);
m_has_invalid_link = true; m_has_invalid_link = true;
return RecursionDecision::Recurse; return RecursionDecision::Recurse;
} }
// Remove leading '/' from the path. // Remove leading '/' from the path.
auto file = DeprecatedString::formatted("{}/Base/usr/share/man/man{}.md", m_serenity_source_directory, url.path().substring(1)); auto file = DeprecatedString::formatted("{}/Base/usr/share/man/man{}.md", m_serenity_source_directory, url.serialize_path().substring(1));
m_file_links.append({ file, DeprecatedString(), StringCollector::from(*link_node.text) }); m_file_links.append({ file, DeprecatedString(), StringCollector::from(*link_node.text) });
return RecursionDecision::Recurse; return RecursionDecision::Recurse;
} }
if (url.scheme() == "file") { if (url.scheme() == "file") {
if (url.path().contains("man"sv) && url.path().ends_with(".md"sv)) { auto file_path = url.serialize_path();
if (file_path.contains("man"sv) && file_path.ends_with(".md"sv)) {
warnln("Inter-manpage link without the help:// scheme: {}\nPlease use help URLs of the form 'help://man/<section>/<subsection...>/<page>'", href); warnln("Inter-manpage link without the help:// scheme: {}\nPlease use help URLs of the form 'help://man/<section>/<subsection...>/<page>'", href);
m_has_invalid_link = true; m_has_invalid_link = true;
return RecursionDecision::Recurse; return RecursionDecision::Recurse;
} }
// TODO: Check more possible links other than icons. // TODO: Check more possible links other than icons.
if (url.path().starts_with("/res/icons/"sv)) { if (file_path.starts_with("/res/icons/"sv)) {
auto file = DeprecatedString::formatted("{}/Base{}", m_serenity_source_directory, url.path()); auto file = DeprecatedString::formatted("{}/Base{}", m_serenity_source_directory, file_path);
m_file_links.append({ file, DeprecatedString(), StringCollector::from(*link_node.text) }); m_file_links.append({ file, DeprecatedString(), StringCollector::from(*link_node.text) });
} else if (url.path().starts_with("/bin"sv)) { } else if (file_path.starts_with("/bin"sv)) {
StringBuilder builder; StringBuilder builder;
link_node.text->render_to_html(builder); link_node.text->render_to_html(builder);
auto link_text = builder.string_view(); auto link_text = builder.string_view();

View file

@ -324,7 +324,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
} }
if (output_name.is_empty()) if (output_name.is_empty())
output_name = url.path(); output_name = url.serialize_path();
LexicalPath path { output_name }; LexicalPath path { output_name };
output_name = path.basename(); output_name = path.basename();

View file

@ -371,7 +371,7 @@ static auto parse(StringView contents)
if (url.scheme() != "file") if (url.scheme() != "file")
return Error::from_string_literal("NYI: Nonlocal entity"); return Error::from_string_literal("NYI: Nonlocal entity");
auto file = TRY(Core::File::open(url.path(), Core::File::OpenMode::Read)); auto file = TRY(Core::File::open(url.serialize_path(), Core::File::OpenMode::Read));
return DeprecatedString::copy(TRY(file->read_until_eof())); return DeprecatedString::copy(TRY(file->read_until_eof()));
}, },
}, },
@ -440,28 +440,29 @@ static void do_run_tests(XML::Document& document)
continue; continue;
} }
auto file_result = Core::File::open(url.path(), Core::File::OpenMode::Read); auto file_path = url.serialize_path();
auto file_result = Core::File::open(file_path, Core::File::OpenMode::Read);
if (file_result.is_error()) { if (file_result.is_error()) {
warnln("Read error for {}: {}", url.path(), file_result.error()); warnln("Read error for {}: {}", file_path, file_result.error());
s_test_results.set(url.path(), TestResult::RunnerFailed); s_test_results.set(file_path, TestResult::RunnerFailed);
continue; continue;
} }
warnln("Running test {}", url.path()); warnln("Running test {}", file_path);
auto contents = file_result.value()->read_until_eof(); auto contents = file_result.value()->read_until_eof();
if (contents.is_error()) { if (contents.is_error()) {
warnln("Read error for {}: {}", url.path(), contents.error()); warnln("Read error for {}: {}", file_path, contents.error());
s_test_results.set(url.path(), TestResult::RunnerFailed); s_test_results.set(file_path, TestResult::RunnerFailed);
continue; continue;
} }
auto parser = parse(contents.value()); auto parser = parse(contents.value());
auto doc_or_error = parser.parse(); auto doc_or_error = parser.parse();
if (doc_or_error.is_error()) { if (doc_or_error.is_error()) {
if (type == "invalid" || type == "error" || type == "not-wf") if (type == "invalid" || type == "error" || type == "not-wf")
s_test_results.set(url.path(), TestResult::Passed); s_test_results.set(file_path, TestResult::Passed);
else else
s_test_results.set(url.path(), TestResult::Failed); s_test_results.set(file_path, TestResult::Failed);
continue; continue;
} }
@ -471,33 +472,33 @@ static void do_run_tests(XML::Document& document)
auto file_result = Core::File::open(out_path, Core::File::OpenMode::Read); auto file_result = Core::File::open(out_path, Core::File::OpenMode::Read);
if (file_result.is_error()) { if (file_result.is_error()) {
warnln("Read error for {}: {}", out_path, file_result.error()); warnln("Read error for {}: {}", out_path, file_result.error());
s_test_results.set(url.path(), TestResult::RunnerFailed); s_test_results.set(file_path, TestResult::RunnerFailed);
continue; continue;
} }
auto contents = file_result.value()->read_until_eof(); auto contents = file_result.value()->read_until_eof();
if (contents.is_error()) { if (contents.is_error()) {
warnln("Read error for {}: {}", out_path, contents.error()); warnln("Read error for {}: {}", out_path, contents.error());
s_test_results.set(url.path(), TestResult::RunnerFailed); s_test_results.set(file_path, TestResult::RunnerFailed);
continue; continue;
} }
auto parser = parse(contents.value()); auto parser = parse(contents.value());
auto out_doc_or_error = parser.parse(); auto out_doc_or_error = parser.parse();
if (out_doc_or_error.is_error()) { if (out_doc_or_error.is_error()) {
warnln("Parse error for {}: {}", out_path, out_doc_or_error.error()); warnln("Parse error for {}: {}", out_path, out_doc_or_error.error());
s_test_results.set(url.path(), TestResult::RunnerFailed); s_test_results.set(file_path, TestResult::RunnerFailed);
continue; continue;
} }
auto out_doc = out_doc_or_error.release_value(); auto out_doc = out_doc_or_error.release_value();
if (out_doc.root() != doc_or_error.value().root()) { if (out_doc.root() != doc_or_error.value().root()) {
s_test_results.set(url.path(), TestResult::Failed); s_test_results.set(file_path, TestResult::Failed);
continue; continue;
} }
} }
if (type == "invalid" || type == "error" || type == "not-wf") if (type == "invalid" || type == "error" || type == "not-wf")
s_test_results.set(url.path(), TestResult::Failed); s_test_results.set(file_path, TestResult::Failed);
else else
s_test_results.set(url.path(), TestResult::Passed); s_test_results.set(file_path, TestResult::Passed);
} }
} }
} }