mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-22 09:21:57 -05:00
LibURL+Everywhere: Only percent decode URL paths when actually needed
Web specs do not return through javascript percent decoded URL path components - but we were doing this in a number of places due to the default behaviour of URL::serialize_path. Since percent encoded URL paths may not contain valid UTF-8 - this was resulting in us crashing in these places. For example - on an HTMLAnchorElement when retrieving the pathname for the URL of: http://ladybird.org/foo%C2%91%91 To fix this make the URL class only return the percent encoded serialized path, matching the URL spec. When the decoded path is required instead explicitly call URL::percent_decode. This fixes a crash running WPT URL tests for the anchor element on: https://wpt.live/url/a-element.html (cherry picked from commit cc557323326ba55514ef2a8a6e0efd7f09330f06; amended heavily to call `URL::percent_decode()` on all results of `url.serialize_path()` in the rest of serenity -- except in LibGemini, where it looked incorrect, and in LibHTTP, where LadybirdBrowser/ladybird#983 will add it.)
This commit is contained in:
parent
68e58b56c4
commit
65f4aae1d8
42 changed files with 73 additions and 73 deletions
|
@ -159,7 +159,8 @@ TEST_CASE(file_url_with_encoded_characters)
|
|||
URL::URL url("file:///my/file/test%23file.txt"sv);
|
||||
EXPECT(url.is_valid());
|
||||
EXPECT_EQ(url.scheme(), "file");
|
||||
EXPECT_EQ(url.serialize_path(), "/my/file/test#file.txt");
|
||||
EXPECT_EQ(url.serialize_path(), "/my/file/test%23file.txt");
|
||||
EXPECT_EQ(URL::percent_decode(url.serialize_path()), "/my/file/test#file.txt");
|
||||
EXPECT(!url.query().has_value());
|
||||
EXPECT(!url.fragment().has_value());
|
||||
}
|
||||
|
@ -332,7 +333,8 @@ TEST_CASE(unicode)
|
|||
{
|
||||
URL::URL url { "http://example.com/_ünicöde_téxt_©"sv };
|
||||
EXPECT(url.is_valid());
|
||||
EXPECT_EQ(url.serialize_path(), "/_ünicöde_téxt_©");
|
||||
EXPECT_EQ(url.serialize_path(), "/_%C3%BCnic%C3%B6de_t%C3%A9xt_%C2%A9");
|
||||
EXPECT_EQ(URL::percent_decode(url.serialize_path()), "/_ünicöde_téxt_©");
|
||||
EXPECT(!url.query().has_value());
|
||||
EXPECT(!url.fragment().has_value());
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
/foo%C2%91%91
|
|
@ -0,0 +1,8 @@
|
|||
<a id="anchor" href="http://ladybird.org/foo%C2%91%91"></a>
|
||||
<script src="../include.js"></script>
|
||||
<script>
|
||||
test(() => {
|
||||
const anchorElement = document.getElementById('anchor');
|
||||
println(anchorElement.pathname);
|
||||
})
|
||||
</script>
|
|
@ -150,7 +150,7 @@ void GLContextWidget::drop_event(GUI::DropEvent& event)
|
|||
if (url.scheme() != "file")
|
||||
continue;
|
||||
|
||||
auto response = FileSystemAccessClient::Client::the().request_file_read_only_approved(window(), url.serialize_path());
|
||||
auto response = FileSystemAccessClient::Client::the().request_file_read_only_approved(window(), URL::percent_decode(url.serialize_path()));
|
||||
if (response.is_error())
|
||||
return;
|
||||
load_file(response.value().filename(), response.value().release_stream());
|
||||
|
|
|
@ -129,7 +129,7 @@ ErrorOr<bool> handle_drop(GUI::DropEvent const& event, ByteString const& destina
|
|||
|
||||
Vector<ByteString> paths_to_copy;
|
||||
for (auto& url_to_copy : urls) {
|
||||
auto file_path = url_to_copy.serialize_path();
|
||||
auto file_path = URL::percent_decode(url_to_copy.serialize_path());
|
||||
if (!url_to_copy.is_valid() || file_path == target)
|
||||
continue;
|
||||
auto new_path = ByteString::formatted("{}/{}", target, LexicalPath::basename(file_path));
|
||||
|
|
|
@ -202,7 +202,7 @@ void do_paste(ByteString const& target_directory, GUI::Window* window)
|
|||
dbgln("Cannot paste URI {}", uri_as_string);
|
||||
continue;
|
||||
}
|
||||
source_paths.append(url.serialize_path());
|
||||
source_paths.append(URL::percent_decode(url.serialize_path()));
|
||||
}
|
||||
|
||||
if (!source_paths.is_empty()) {
|
||||
|
|
|
@ -1010,7 +1010,7 @@ void MainWidget::drop_event(GUI::DropEvent& event)
|
|||
if (!request_close())
|
||||
return;
|
||||
|
||||
auto file_path = urls.first().serialize_path();
|
||||
auto file_path = URL::percent_decode(urls.first().serialize_path());
|
||||
auto result = FileSystemAccessClient::Client::the().request_file_read_only_approved(window(), file_path);
|
||||
if (result.is_error())
|
||||
return;
|
||||
|
|
|
@ -120,7 +120,7 @@ ErrorOr<void> MainWidget::initialize(GUI::Window& window)
|
|||
m_web_view->use_native_user_style_sheet();
|
||||
m_web_view->on_link_click = [this](auto& url, auto&, unsigned) {
|
||||
if (url.scheme() == "file") {
|
||||
auto path = LexicalPath { url.serialize_path() };
|
||||
auto path = LexicalPath { URL::percent_decode(url.serialize_path()) };
|
||||
if (!path.is_child_of(Manual::manual_base_path)) {
|
||||
open_external(url);
|
||||
return;
|
||||
|
@ -253,7 +253,7 @@ void MainWidget::open_url(URL::URL const& url)
|
|||
if (url.scheme() == "file") {
|
||||
m_web_view->load(url);
|
||||
|
||||
auto browse_view_index = m_manual_model->index_from_path(url.serialize_path());
|
||||
auto browse_view_index = m_manual_model->index_from_path(URL::percent_decode(url.serialize_path()));
|
||||
if (browse_view_index.has_value()) {
|
||||
if (browse_view_index.value() != m_browse_view->selection_start_index()) {
|
||||
m_browse_view->expand_all_parents_of(browse_view_index.value());
|
||||
|
|
|
@ -851,7 +851,7 @@ void HexEditorWidget::drop_event(GUI::DropEvent& event)
|
|||
return;
|
||||
|
||||
// TODO: A drop event should be considered user consent for opening a file
|
||||
auto response = FileSystemAccessClient::Client::the().request_file(window(), urls.first().serialize_path(), Core::File::OpenMode::Read);
|
||||
auto response = FileSystemAccessClient::Client::the().request_file(window(), URL::percent_decode(urls.first().serialize_path()), Core::File::OpenMode::Read);
|
||||
if (response.is_error())
|
||||
return;
|
||||
open_file(response.value().filename(), response.value().release_stream());
|
||||
|
|
|
@ -100,7 +100,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|||
|
||||
window->move_to_front();
|
||||
|
||||
auto path = urls.first().serialize_path();
|
||||
auto path = URL::percent_decode(urls.first().serialize_path());
|
||||
auto result = FileSystemAccessClient::Client::the().request_file_read_only_approved(window, path);
|
||||
if (result.is_error())
|
||||
return;
|
||||
|
@ -109,7 +109,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|||
widget.open_file(MUST(String::from_byte_string(value.filename())), value.stream());
|
||||
|
||||
for (size_t i = 1; i < urls.size(); ++i) {
|
||||
Desktop::Launcher::open(URL::create_with_file_scheme(urls[i].serialize_path().characters()), "/bin/ImageViewer");
|
||||
Desktop::Launcher::open(URL::create_with_file_scheme(URL::percent_decode(urls[i].serialize_path()).characters()), "/bin/ImageViewer");
|
||||
}
|
||||
};
|
||||
widget.on_doubleclick = [&] {
|
||||
|
|
|
@ -542,7 +542,7 @@ void PDFViewerWidget::drop_event(GUI::DropEvent& event)
|
|||
return;
|
||||
}
|
||||
|
||||
auto response = FileSystemAccessClient::Client::the().request_file_read_only_approved(window(), urls.first().serialize_path());
|
||||
auto response = FileSystemAccessClient::Client::the().request_file_read_only_approved(window(), URL::percent_decode(urls.first().serialize_path()));
|
||||
if (response.is_error())
|
||||
return;
|
||||
if (auto result = try_open_file(response.value().filename(), response.value().release_stream()); result.is_error())
|
||||
|
|
|
@ -1511,7 +1511,7 @@ void MainWidget::drop_event(GUI::DropEvent& event)
|
|||
if (url.scheme() != "file")
|
||||
continue;
|
||||
|
||||
auto response = FileSystemAccessClient::Client::the().request_file(window(), url.serialize_path(), Core::File::OpenMode::Read);
|
||||
auto response = FileSystemAccessClient::Client::the().request_file(window(), URL::percent_decode(url.serialize_path()), Core::File::OpenMode::Read);
|
||||
if (response.is_error())
|
||||
return;
|
||||
open_image(response.release_value());
|
||||
|
|
|
@ -220,6 +220,6 @@ void PresenterWidget::drop_event(GUI::DropEvent& event)
|
|||
return;
|
||||
|
||||
window()->move_to_front();
|
||||
set_file(urls.first().serialize_path());
|
||||
set_file(URL::percent_decode(urls.first().serialize_path()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -143,7 +143,7 @@ bool RunWindow::run_via_launch(ByteString const& run_input)
|
|||
auto url = URL::create_with_url_or_path(run_input);
|
||||
|
||||
if (url.scheme() == "file") {
|
||||
auto file_path = url.serialize_path();
|
||||
auto file_path = URL::percent_decode(url.serialize_path());
|
||||
auto real_path_or_error = FileSystem::real_path(file_path);
|
||||
if (real_path_or_error.is_error()) {
|
||||
warnln("Failed to launch '{}': {}", file_path, real_path_or_error.error());
|
||||
|
|
|
@ -162,7 +162,7 @@ void SoundPlayerWidget::drop_event(GUI::DropEvent& event)
|
|||
return;
|
||||
window()->move_to_front();
|
||||
// FIXME: Add all paths from drop event to the playlist
|
||||
play_file_path(urls.first().serialize_path());
|
||||
play_file_path(URL::percent_decode(urls.first().serialize_path()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ HelpWindow::HelpWindow(GUI::Window* parent)
|
|||
m_webview->on_link_click = [this](auto& url, auto&, auto&&) {
|
||||
VERIFY(url.scheme() == "spreadsheet");
|
||||
if (url.host().template has<String>() && url.host().template get<String>() == "example"sv) {
|
||||
auto example_path = url.serialize_path();
|
||||
auto example_path = URL::percent_decode(url.serialize_path());
|
||||
auto entry = LexicalPath::basename(example_path);
|
||||
auto doc_option = m_docs.get_object(entry);
|
||||
if (!doc_option.has_value()) {
|
||||
|
@ -125,7 +125,7 @@ HelpWindow::HelpWindow(GUI::Window* parent)
|
|||
widget->add_sheet(sheet.release_nonnull());
|
||||
window->show();
|
||||
} else if (url.host() == "doc"_string) {
|
||||
auto entry = LexicalPath::basename(url.serialize_path());
|
||||
auto entry = LexicalPath::basename(URL::percent_decode(url.serialize_path()));
|
||||
m_webview->load(URL::create_with_data("text/html"sv, render(entry)));
|
||||
} else {
|
||||
dbgln("Invalid spreadsheet action domain '{}'", url.serialized_host().release_value_but_fixme_should_propagate_errors());
|
||||
|
|
|
@ -265,7 +265,7 @@ Optional<Position> Sheet::position_from_url(const URL::URL& url) const
|
|||
}
|
||||
|
||||
// FIXME: Figure out a way to do this cross-process.
|
||||
VERIFY(url.serialize_path() == ByteString::formatted("/{}", getpid()));
|
||||
VERIFY(URL::percent_decode(url.serialize_path()) == ByteString::formatted("/{}", getpid()));
|
||||
|
||||
return parse_cell_name(url.fragment().value_or(String {}));
|
||||
}
|
||||
|
|
|
@ -856,7 +856,7 @@ void MainWidget::drop_event(GUI::DropEvent& event)
|
|||
if (!request_close())
|
||||
return;
|
||||
|
||||
auto response = FileSystemAccessClient::Client::the().request_file_read_only_approved(window(), urls.first().serialize_path());
|
||||
auto response = FileSystemAccessClient::Client::the().request_file_read_only_approved(window(), URL::percent_decode(urls.first().serialize_path()));
|
||||
if (response.is_error())
|
||||
return;
|
||||
if (auto result = read_file(response.value().filename(), response.value().stream()); result.is_error())
|
||||
|
|
|
@ -703,7 +703,7 @@ void MainWidget::drop_event(GUI::DropEvent& event)
|
|||
if (request_close() == GUI::Window::CloseRequestDecision::StayOpen)
|
||||
return;
|
||||
|
||||
auto response = FileSystemAccessClient::Client::the().request_file(window(), urls.first().serialize_path(), Core::File::OpenMode::Read);
|
||||
auto response = FileSystemAccessClient::Client::the().request_file(window(), URL::percent_decode(urls.first().serialize_path()), Core::File::OpenMode::Read);
|
||||
if (response.is_error())
|
||||
return;
|
||||
|
||||
|
|
|
@ -284,7 +284,7 @@ void VideoPlayerWidget::drop_event(GUI::DropEvent& event)
|
|||
GUI::MessageBox::show_error(window(), "VideoPlayer can only view one clip at a time!"sv);
|
||||
return;
|
||||
}
|
||||
auto response = FileSystemAccessClient::Client::the().request_file_read_only_approved(window(), urls.first().serialize_path());
|
||||
auto response = FileSystemAccessClient::Client::the().request_file_read_only_approved(window(), URL::percent_decode(urls.first().serialize_path()));
|
||||
if (response.is_error())
|
||||
return;
|
||||
|
||||
|
|
|
@ -341,7 +341,7 @@ void MainWidget::drop_event(GUI::DropEvent& event)
|
|||
if (request_close() == GUI::Window::CloseRequestDecision::StayOpen)
|
||||
return;
|
||||
|
||||
auto response = FileSystemAccessClient::Client::the().request_file_read_only_approved(window(), urls.first().serialize_path());
|
||||
auto response = FileSystemAccessClient::Client::the().request_file_read_only_approved(window(), URL::percent_decode(urls.first().serialize_path()));
|
||||
if (response.is_error())
|
||||
return;
|
||||
load_file(response.release_value());
|
||||
|
|
|
@ -365,7 +365,7 @@ void Editor::drop_event(GUI::DropEvent& event)
|
|||
return;
|
||||
}
|
||||
set_current_editor_wrapper(static_cast<EditorWrapper*>(parent()));
|
||||
open_file(urls.first().serialize_path());
|
||||
open_file(URL::percent_decode(urls.first().serialize_path()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -483,7 +483,7 @@ void MainWidget::drop_event(GUI::DropEvent& drop_event)
|
|||
if (!scheme.bytes_as_string_view().equals_ignoring_ascii_case("file"sv))
|
||||
continue;
|
||||
|
||||
auto lexical_path = LexicalPath(url.serialize_path());
|
||||
auto lexical_path = LexicalPath(URL::percent_decode(url.serialize_path()));
|
||||
open_script_from_file(lexical_path);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -236,12 +236,12 @@ bool is_special_scheme(StringView scheme)
|
|||
}
|
||||
|
||||
// https://url.spec.whatwg.org/#url-path-serializer
|
||||
ByteString URL::serialize_path(ApplyPercentDecoding apply_percent_decoding) const
|
||||
String URL::serialize_path() const
|
||||
{
|
||||
// 1. If url has an opaque path, then return url’s path.
|
||||
// FIXME: Reimplement this step once we modernize the URL implementation to meet the spec.
|
||||
if (cannot_be_a_base_url())
|
||||
return m_data->paths[0].to_byte_string();
|
||||
return m_data->paths[0];
|
||||
|
||||
// 2. Let output be the empty string.
|
||||
StringBuilder output;
|
||||
|
@ -249,11 +249,11 @@ ByteString URL::serialize_path(ApplyPercentDecoding apply_percent_decoding) cons
|
|||
// 3. For each segment of url’s path: append U+002F (/) followed by segment to output.
|
||||
for (auto const& segment : m_data->paths) {
|
||||
output.append('/');
|
||||
output.append(apply_percent_decoding == ApplyPercentDecoding::Yes ? percent_decode(segment) : segment.to_byte_string());
|
||||
output.append(segment);
|
||||
}
|
||||
|
||||
// 4. Return output.
|
||||
return output.to_byte_string();
|
||||
return output.to_string_without_validation();
|
||||
}
|
||||
|
||||
// https://url.spec.whatwg.org/#concept-url-serializer
|
||||
|
|
|
@ -54,11 +54,6 @@ using IPv6Address = Array<u16, 8>;
|
|||
// but it is sometimes used as opaque identifier in URLs where a network address is not necessary.
|
||||
using Host = Variant<IPv4Address, IPv6Address, String, Empty>;
|
||||
|
||||
enum class ApplyPercentDecoding {
|
||||
Yes,
|
||||
No
|
||||
};
|
||||
|
||||
// https://w3c.github.io/FileAPI/#blob-url-entry
|
||||
// NOTE: This represents the raw bytes behind a 'Blob' (and does not yet support a MediaSourceQuery).
|
||||
struct BlobURLEntry {
|
||||
|
@ -134,7 +129,7 @@ public:
|
|||
m_data->paths.append(String {});
|
||||
}
|
||||
|
||||
ByteString serialize_path(ApplyPercentDecoding = ApplyPercentDecoding::Yes) const;
|
||||
String serialize_path() const;
|
||||
ByteString serialize(ExcludeFragment = ExcludeFragment::No) const;
|
||||
ByteString serialize_for_display() const;
|
||||
ByteString to_byte_string() const { return serialize(); }
|
||||
|
|
|
@ -889,7 +889,7 @@ void TerminalWidget::mousemove_event(GUI::MouseEvent& event)
|
|||
auto handlers = Desktop::Launcher::get_handlers_for_url(attribute.href);
|
||||
if (!handlers.is_empty()) {
|
||||
auto url = URL::URL(attribute.href);
|
||||
auto path = url.serialize_path();
|
||||
auto path = URL::percent_decode(url.serialize_path());
|
||||
|
||||
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]);
|
||||
|
@ -1184,7 +1184,7 @@ void TerminalWidget::context_menu_event(GUI::ContextMenuEvent& event)
|
|||
}));
|
||||
m_context_menu_for_hyperlink->add_action(GUI::Action::create("Copy &Name", [&](auto&) {
|
||||
// file://courage/home/anon/something -> /home/anon/something
|
||||
auto path = URL::URL(m_context_menu_href).serialize_path();
|
||||
auto path = URL::percent_decode(URL::URL(m_context_menu_href).serialize_path());
|
||||
// /home/anon/something -> something
|
||||
auto name = LexicalPath::basename(path);
|
||||
GUI::Clipboard::the().set_plain_text(name);
|
||||
|
@ -1215,7 +1215,7 @@ void TerminalWidget::drop_event(GUI::DropEvent& event)
|
|||
send_non_user_input(" "sv.bytes());
|
||||
|
||||
if (url.scheme() == "file") {
|
||||
auto path = url.serialize_path();
|
||||
auto path = URL::percent_decode(url.serialize_path());
|
||||
send_non_user_input(path.bytes());
|
||||
} else {
|
||||
auto url_string = url.to_byte_string();
|
||||
|
|
|
@ -363,12 +363,10 @@ void DOMURL::set_port(String const& port)
|
|||
}
|
||||
|
||||
// https://url.spec.whatwg.org/#dom-url-pathname
|
||||
WebIDL::ExceptionOr<String> DOMURL::pathname() const
|
||||
String DOMURL::pathname() const
|
||||
{
|
||||
auto& vm = realm().vm();
|
||||
|
||||
// The pathname getter steps are to return the result of URL path serializing this’s URL.
|
||||
return TRY_OR_THROW_OOM(vm, String::from_byte_string(m_url.serialize_path(URL::ApplyPercentDecoding::No)));
|
||||
return m_url.serialize_path();
|
||||
}
|
||||
|
||||
// https://url.spec.whatwg.org/#ref-for-dom-url-pathname%E2%91%A0
|
||||
|
|
|
@ -55,7 +55,7 @@ public:
|
|||
WebIDL::ExceptionOr<String> port() const;
|
||||
void set_port(String const&);
|
||||
|
||||
WebIDL::ExceptionOr<String> pathname() const;
|
||||
String pathname() const;
|
||||
void set_pathname(String const&);
|
||||
|
||||
Optional<String> const& fragment() const { return m_url.fragment(); }
|
||||
|
|
|
@ -284,10 +284,8 @@ String HTMLHyperlinkElementUtils::pathname() const
|
|||
if (!m_url.has_value())
|
||||
return String {};
|
||||
|
||||
// 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.
|
||||
// 6. Return "/", followed by the strings in url's path (including empty strings), separated from each other by "/".
|
||||
return MUST(String::from_byte_string(m_url->serialize_path()));
|
||||
// 4. Return the result of URL path serializing url.
|
||||
return m_url->serialize_path();
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/links.html#dom-hyperlink-pathname
|
||||
|
|
|
@ -277,15 +277,13 @@ WebIDL::ExceptionOr<void> Location::set_port(String const&)
|
|||
// https://html.spec.whatwg.org/multipage/history.html#dom-location-pathname
|
||||
WebIDL::ExceptionOr<String> Location::pathname() const
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
|
||||
// 1. If this's relevant Document is non-null and its origin is not same origin-domain with the entry settings object's origin, then throw a "SecurityError" DOMException.
|
||||
auto const relevant_document = this->relevant_document();
|
||||
if (relevant_document && !relevant_document->origin().is_same_origin_domain(entry_settings_object().origin()))
|
||||
return WebIDL::SecurityError::create(realm(), "Location's relevant document is not same origin-domain with the entry settings object's origin"_fly_string);
|
||||
|
||||
// 2. Return the result of URL path serializing this Location object's url.
|
||||
return TRY_OR_THROW_OOM(vm, String::from_byte_string(url().serialize_path()));
|
||||
return url().serialize_path();
|
||||
}
|
||||
|
||||
WebIDL::ExceptionOr<void> Location::set_pathname(String const&)
|
||||
|
|
|
@ -92,11 +92,10 @@ WebIDL::ExceptionOr<String> WorkerLocation::port() const
|
|||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/workers.html#dom-workerlocation-pathname
|
||||
WebIDL::ExceptionOr<String> WorkerLocation::pathname() const
|
||||
String WorkerLocation::pathname() const
|
||||
{
|
||||
auto& vm = realm().vm();
|
||||
// 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_byte_string(m_global_scope->url().serialize_path()));
|
||||
return m_global_scope->url().serialize_path();
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/workers.html#dom-workerlocation-search
|
||||
|
|
|
@ -24,7 +24,7 @@ public:
|
|||
WebIDL::ExceptionOr<String> host() const;
|
||||
WebIDL::ExceptionOr<String> hostname() const;
|
||||
WebIDL::ExceptionOr<String> port() const;
|
||||
WebIDL::ExceptionOr<String> pathname() const;
|
||||
String pathname() const;
|
||||
WebIDL::ExceptionOr<String> search() const;
|
||||
WebIDL::ExceptionOr<String> hash() const;
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ ErrorOr<String> load_error_page(URL::URL const& url, StringView error_message)
|
|||
ErrorOr<String> load_file_directory_page(URL::URL const& url)
|
||||
{
|
||||
// Generate HTML contents entries table
|
||||
auto lexical_path = LexicalPath(url.serialize_path());
|
||||
auto lexical_path = LexicalPath(URL::percent_decode(url.serialize_path()));
|
||||
Core::DirIterator dt(lexical_path.string(), Core::DirIterator::Flags::SkipParentAndBaseDir);
|
||||
Vector<ByteString> names;
|
||||
while (dt.has_next())
|
||||
|
|
|
@ -100,14 +100,14 @@ void Resource::did_load(Badge<ResourceLoader>, ReadonlyBytes data, HTTP::HeaderM
|
|||
// 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.
|
||||
// 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().serialize_path().ends_with(".qoi"sv))
|
||||
if (m_mime_type == "application/octet-stream" && URL::percent_decode(url().serialize_path()).ends_with(".qoi"sv))
|
||||
m_mime_type = "image/x-qoi";
|
||||
} else {
|
||||
auto content_type_options = headers.get("X-Content-Type-Options");
|
||||
if (content_type_options.value_or("").equals_ignoring_ascii_case("nosniff"sv)) {
|
||||
m_mime_type = "text/plain";
|
||||
} else {
|
||||
m_mime_type = Core::guess_mime_type_based_on_filename(url().serialize_path());
|
||||
m_mime_type = Core::guess_mime_type_based_on_filename(URL::percent_decode(url().serialize_path()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -329,7 +329,7 @@ void ResourceLoader::load(LoadRequest& request, SuccessCallback success_callback
|
|||
}
|
||||
|
||||
auto data = resource.value()->data();
|
||||
auto response_headers = response_headers_for_file(url.serialize_path(), resource.value()->modified_time());
|
||||
auto response_headers = response_headers_for_file(URL::percent_decode(url.serialize_path()), resource.value()->modified_time());
|
||||
|
||||
log_success(request);
|
||||
success_callback(data, response_headers, {});
|
||||
|
@ -346,7 +346,7 @@ void ResourceLoader::load(LoadRequest& request, SuccessCallback success_callback
|
|||
return;
|
||||
}
|
||||
|
||||
FileRequest file_request(url.serialize_path(), [this, success_callback = move(success_callback), error_callback = move(error_callback), request, respond_directory_page](ErrorOr<i32> file_or_error) {
|
||||
FileRequest file_request(URL::percent_decode(url.serialize_path()), [this, success_callback = move(success_callback), error_callback = move(error_callback), request, respond_directory_page](ErrorOr<i32> file_or_error) {
|
||||
--m_pending_loads;
|
||||
if (on_load_counter_change)
|
||||
on_load_counter_change();
|
||||
|
@ -394,7 +394,7 @@ void ResourceLoader::load(LoadRequest& request, SuccessCallback success_callback
|
|||
}
|
||||
|
||||
auto data = maybe_data.release_value();
|
||||
auto response_headers = response_headers_for_file(request.url().serialize_path(), st_or_error.value().st_mtime);
|
||||
auto response_headers = response_headers_for_file(URL::percent_decode(request.url().serialize_path()), st_or_error.value().st_mtime);
|
||||
|
||||
log_success(request);
|
||||
success_callback(data, response_headers, {});
|
||||
|
|
|
@ -26,7 +26,7 @@ ByteString ConnectionInfo::resource_name() const
|
|||
// The "resource-name" can be constructed by concatenating the following:
|
||||
StringBuilder builder;
|
||||
// "/" if the path component is empty
|
||||
auto path = m_url.serialize_path();
|
||||
auto path = URL::percent_decode(m_url.serialize_path());
|
||||
if (path.is_empty())
|
||||
builder.append('/');
|
||||
// The path component
|
||||
|
|
|
@ -287,7 +287,7 @@ String CookieJar::default_path(const URL::URL& url)
|
|||
// 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).
|
||||
auto uri_path = url.serialize_path();
|
||||
auto uri_path = URL::percent_decode(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.
|
||||
if (uri_path.is_empty() || (uri_path[0] != '/'))
|
||||
|
@ -301,6 +301,7 @@ String CookieJar::default_path(const URL::URL& url)
|
|||
return "/"_string;
|
||||
|
||||
// 4. Output the characters of the uri-path from the first character up to, but not including, the right-most %x2F ("/").
|
||||
// FIXME: The path might not be valid UTF-8.
|
||||
return MUST(String::from_utf8(uri_path.substring_view(0, last_separator)));
|
||||
}
|
||||
|
||||
|
|
|
@ -149,7 +149,7 @@ Vector<ByteString> Launcher::handlers_for_url(const URL::URL& url)
|
|||
{
|
||||
Vector<ByteString> handlers;
|
||||
if (url.scheme() == "file") {
|
||||
for_each_handler_for_path(url.serialize_path(), [&](auto& handler) -> bool {
|
||||
for_each_handler_for_path(URL::percent_decode(url.serialize_path()), [&](auto& handler) -> bool {
|
||||
handlers.append(handler.executable);
|
||||
return true;
|
||||
});
|
||||
|
@ -169,7 +169,7 @@ Vector<ByteString> Launcher::handlers_with_details_for_url(const URL::URL& url)
|
|||
{
|
||||
Vector<ByteString> handlers;
|
||||
if (url.scheme() == "file") {
|
||||
for_each_handler_for_path(url.serialize_path(), [&](auto& handler) -> bool {
|
||||
for_each_handler_for_path(URL::percent_decode(url.serialize_path()), [&](auto& handler) -> bool {
|
||||
handlers.append(handler.to_details_str());
|
||||
return true;
|
||||
});
|
||||
|
@ -214,7 +214,7 @@ bool Launcher::open_with_handler_name(const URL::URL& url, ByteString const& han
|
|||
auto& handler = handler_optional.value();
|
||||
ByteString argument;
|
||||
if (url.scheme() == "file")
|
||||
argument = url.serialize_path();
|
||||
argument = URL::percent_decode(url.serialize_path());
|
||||
else
|
||||
argument = url.to_byte_string();
|
||||
return spawn(handler.executable, { argument });
|
||||
|
@ -357,7 +357,7 @@ void Launcher::for_each_handler_for_path(ByteString const& path, Function<bool(H
|
|||
bool Launcher::open_file_url(const URL::URL& url)
|
||||
{
|
||||
struct stat st;
|
||||
auto file_path = url.serialize_path();
|
||||
auto file_path = URL::percent_decode(url.serialize_path());
|
||||
if (stat(file_path.characters(), &st) < 0) {
|
||||
perror("stat");
|
||||
return false;
|
||||
|
@ -399,7 +399,7 @@ bool Launcher::open_file_url(const URL::URL& url)
|
|||
|
||||
// Additional parameters parsing, specific for the file protocol and txt file handlers
|
||||
Vector<ByteString> additional_parameters;
|
||||
ByteString filepath = url.serialize_path();
|
||||
ByteString filepath = URL::percent_decode(url.serialize_path());
|
||||
|
||||
if (url.query().has_value()) {
|
||||
url.query()->bytes_as_string_view().for_each_split_view('&', SplitBehavior::Nothing, [&](auto parameter) {
|
||||
|
|
|
@ -164,7 +164,7 @@ void QuickLaunchWidget::drop_event(GUI::DropEvent& event)
|
|||
if (event.mime_data().has_urls()) {
|
||||
auto urls = event.mime_data().urls();
|
||||
for (auto& url : urls) {
|
||||
auto path = url.serialize_path();
|
||||
auto path = URL::percent_decode(url.serialize_path());
|
||||
auto entry = QuickLaunchEntry::create_from_path(path);
|
||||
if (entry) {
|
||||
auto entry_name = entry->name();
|
||||
|
|
|
@ -204,13 +204,13 @@ RecursionDecision MarkdownLinkage::visit(Markdown::Text::LinkNode const& link_no
|
|||
}
|
||||
|
||||
// Remove leading '/' from the path.
|
||||
auto file = ByteString::formatted("{}/Base/usr/share/man/man{}.md", m_serenity_source_directory, url.serialize_path().substring(1));
|
||||
auto file = ByteString::formatted("{}/Base/usr/share/man/man{}.md", m_serenity_source_directory, URL::percent_decode(url.serialize_path()).substring(1));
|
||||
|
||||
m_file_links.append({ file, ByteString(), StringCollector::from(*link_node.text) });
|
||||
return RecursionDecision::Recurse;
|
||||
}
|
||||
if (url.scheme() == "file") {
|
||||
auto file_path = url.serialize_path();
|
||||
auto file_path = URL::percent_decode(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);
|
||||
m_has_invalid_link = true;
|
||||
|
|
|
@ -331,7 +331,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|||
}
|
||||
|
||||
if (output_name.is_empty())
|
||||
output_name = url.serialize_path();
|
||||
output_name = URL::percent_decode(url.serialize_path());
|
||||
|
||||
LexicalPath path { output_name };
|
||||
output_name = path.basename();
|
||||
|
|
|
@ -371,7 +371,7 @@ static auto parse(StringView contents)
|
|||
if (url.scheme() != "file")
|
||||
return Error::from_string_literal("NYI: Nonlocal entity");
|
||||
|
||||
auto file = TRY(Core::File::open(url.serialize_path(), Core::File::OpenMode::Read));
|
||||
auto file = TRY(Core::File::open(URL::percent_decode(url.serialize_path()), Core::File::OpenMode::Read));
|
||||
return ByteString::copy(TRY(file->read_until_eof()));
|
||||
},
|
||||
},
|
||||
|
@ -440,7 +440,7 @@ static void do_run_tests(XML::Document& document)
|
|||
continue;
|
||||
}
|
||||
|
||||
auto file_path = url.serialize_path();
|
||||
auto file_path = URL::percent_decode(url.serialize_path());
|
||||
auto file_result = Core::File::open(file_path, Core::File::OpenMode::Read);
|
||||
if (file_result.is_error()) {
|
||||
warnln("Read error for {}: {}", file_path, file_result.error());
|
||||
|
|
Loading…
Reference in a new issue