From 6dd716adf2df53b911d3af532299023a28810bcb Mon Sep 17 00:00:00 2001 From: Lucas CHOLLET Date: Wed, 7 Dec 2022 23:40:39 +0100 Subject: [PATCH] LibFileSystemAccessClient: Add `save_file()` This method replaces `try_save_file_deprecated()`, as it has the same behavior but returns a `Core::Stream::File` instead. --- .../LibFileSystemAccessClient/Client.cpp | 89 +++++++++++++------ .../LibFileSystemAccessClient/Client.h | 11 ++- 2 files changed, 73 insertions(+), 27 deletions(-) diff --git a/Userland/Libraries/LibFileSystemAccessClient/Client.cpp b/Userland/Libraries/LibFileSystemAccessClient/Client.cpp index fff5efaa55..40f3590ef6 100644 --- a/Userland/Libraries/LibFileSystemAccessClient/Client.cpp +++ b/Userland/Libraries/LibFileSystemAccessClient/Client.cpp @@ -26,7 +26,7 @@ Client& Client::the() DeprecatedResult Client::try_request_file_read_only_approved(GUI::Window* parent_window, DeprecatedString const& path) { auto const id = get_new_id(); - m_promises.set(id, PromiseAndWindow { Core::Promise::construct(), parent_window }); + m_promises.set(id, PromiseAndWindow { { Core::Promise::construct() }, parent_window }); auto parent_window_server_client_id = GUI::ConnectionToWindowServer::the().expose_client_id(); auto child_window_server_client_id = expose_window_server_client_id(); @@ -45,13 +45,13 @@ DeprecatedResult Client::try_request_file_read_only_approved(GUI::Window* parent async_request_file_read_only_approved(id, parent_window_server_client_id, parent_window_id, full_path); } - return handle_promise(id); + return handle_promise(id); } DeprecatedResult Client::try_request_file(GUI::Window* parent_window, DeprecatedString const& path, Core::OpenMode mode) { auto const id = get_new_id(); - m_promises.set(id, PromiseAndWindow { Core::Promise::construct(), parent_window }); + m_promises.set(id, PromiseAndWindow { { Core::Promise::construct() }, parent_window }); auto parent_window_server_client_id = GUI::ConnectionToWindowServer::the().expose_client_id(); auto child_window_server_client_id = expose_window_server_client_id(); @@ -70,13 +70,13 @@ DeprecatedResult Client::try_request_file(GUI::Window* parent_window, Deprecated async_request_file(id, parent_window_server_client_id, parent_window_id, full_path, mode); } - return handle_promise(id); + return handle_promise(id); } DeprecatedResult Client::try_open_file(GUI::Window* parent_window, DeprecatedString const& window_title, StringView path, Core::OpenMode requested_access) { auto const id = get_new_id(); - m_promises.set(id, PromiseAndWindow { Core::Promise::construct(), parent_window }); + m_promises.set(id, PromiseAndWindow { { Core::Promise::construct() }, parent_window }); auto parent_window_server_client_id = GUI::ConnectionToWindowServer::the().expose_client_id(); auto child_window_server_client_id = expose_window_server_client_id(); @@ -90,13 +90,13 @@ DeprecatedResult Client::try_open_file(GUI::Window* parent_window, DeprecatedStr async_prompt_open_file(id, parent_window_server_client_id, parent_window_id, window_title, path, requested_access); - return handle_promise(id); + return handle_promise(id); } DeprecatedResult Client::try_save_file_deprecated(GUI::Window* parent_window, DeprecatedString const& name, DeprecatedString const ext, Core::OpenMode requested_access) { auto const id = get_new_id(); - m_promises.set(id, PromiseAndWindow { Core::Promise::construct(), parent_window }); + m_promises.set(id, PromiseAndWindow { { Core::Promise::construct() }, parent_window }); auto parent_window_server_client_id = GUI::ConnectionToWindowServer::the().expose_client_id(); auto child_window_server_client_id = expose_window_server_client_id(); @@ -110,7 +110,30 @@ DeprecatedResult Client::try_save_file_deprecated(GUI::Window* parent_window, De async_prompt_save_file(id, parent_window_server_client_id, parent_window_id, name.is_null() ? "Untitled" : name, ext.is_null() ? "txt" : ext, Core::StandardPaths::home_directory(), requested_access); - return handle_promise(id); + return handle_promise(id); +} + +Result Client::save_file(GUI::Window* parent_window, DeprecatedString const& name, DeprecatedString const ext, Core::Stream::OpenMode requested_access) +{ + auto const id = get_new_id(); + m_promises.set(id, PromiseAndWindow { { Core::Promise::construct() }, parent_window }); + + auto parent_window_server_client_id = GUI::ConnectionToWindowServer::the().expose_client_id(); + auto child_window_server_client_id = expose_window_server_client_id(); + auto parent_window_id = parent_window->window_id(); + + GUI::ConnectionToWindowServer::the().add_window_stealing_for_client(child_window_server_client_id, parent_window_id); + + ScopeGuard guard([parent_window_id, child_window_server_client_id] { + GUI::ConnectionToWindowServer::the().remove_window_stealing_for_client(child_window_server_client_id, parent_window_id); + }); + + // The endpoint only cares about ReadOnly, WriteOnly and ReadWrite and both enum shares the same layout for these. + Core::OpenMode deprecated_requested_access = static_cast(requested_access); + + async_prompt_save_file(id, parent_window_server_client_id, parent_window_id, name.is_null() ? "Untitled" : name, ext.is_null() ? "txt" : ext, Core::StandardPaths::home_directory(), deprecated_requested_access); + + return handle_promise(id); } void Client::handle_prompt_end(i32 request_id, i32 error, Optional const& ipc_file, Optional const& chosen_file) @@ -118,37 +141,52 @@ void Client::handle_prompt_end(i32 request_id, i32 error, Optional co auto potential_data = m_promises.get(request_id); VERIFY(potential_data.has_value()); auto& request_data = potential_data.value(); + + auto const resolve_any_promise = [&promise = request_data.promise](Error&& error) { + if (promise.has>()) { + promise.get>()->resolve(move(error)); + return; + } + promise.get>()->resolve(move(error)); + }; + if (error != 0) { // We don't want to show an error message for non-existent files since some applications may want // to handle it as opening a new, named file. if (error != -1 && error != ENOENT) GUI::MessageBox::show_error(request_data.parent_window, DeprecatedString::formatted("Opening \"{}\" failed: {}", *chosen_file, strerror(error))); - request_data.promise->resolve(Error::from_errno(error)); + resolve_any_promise(Error::from_errno(error)); return; } - auto file = Core::File::construct(); - auto fd = ipc_file->take_fd(); - if (!file->open(fd, Core::OpenMode::ReadWrite, Core::File::ShouldCloseFileDescriptor::Yes) && file->error() != ENOENT) { - GUI::MessageBox::show_error(request_data.parent_window, DeprecatedString::formatted("Opening \"{}\" failed: {}", *chosen_file, strerror(error))); - request_data.promise->resolve(Error::from_errno(file->error())); - return; - } - - if (file->is_device()) { + if (Core::File::is_device(ipc_file->fd())) { GUI::MessageBox::show_error(request_data.parent_window, DeprecatedString::formatted("Opening \"{}\" failed: Cannot open device files", *chosen_file)); - request_data.promise->resolve(Error::from_string_literal("Cannot open device files")); + resolve_any_promise(Error::from_string_literal("Cannot open device files")); return; } - if (file->is_directory()) { + if (Core::File::is_directory(ipc_file->fd())) { GUI::MessageBox::show_error(request_data.parent_window, DeprecatedString::formatted("Opening \"{}\" failed: Cannot open directory", *chosen_file)); - request_data.promise->resolve(Error::from_errno(EISDIR)); + resolve_any_promise(Error::from_errno(EISDIR)); return; } - file->set_filename(move(*chosen_file)); - request_data.promise->resolve(file); + if (request_data.promise.has>()) { + auto file = Core::File::construct(); + auto fd = ipc_file->take_fd(); + file->open(fd, Core::OpenMode::ReadWrite, Core::File::ShouldCloseFileDescriptor::Yes); + file->set_filename(*chosen_file); + + request_data.promise.get>()->resolve(file); + return; + } + + auto file_or_error = Core::Stream::File::adopt_fd(ipc_file->take_fd(), Core::Stream::OpenMode::ReadWrite); + if (file_or_error.is_error()) { + resolve_any_promise(file_or_error.release_error()); + } + + request_data.promise.get>()->resolve(file_or_error.release_value()); } void Client::die() @@ -166,9 +204,10 @@ int Client::get_new_id() return new_id; } -DeprecatedResult Client::handle_promise(int id) +template +AnyResult Client::handle_promise(int id) { - auto result = m_promises.get(id)->promise->await(); + auto result = m_promises.get(id)->promise.get>()->await(); m_promises.remove(id); return result; } diff --git a/Userland/Libraries/LibFileSystemAccessClient/Client.h b/Userland/Libraries/LibFileSystemAccessClient/Client.h index d63713dc63..43ae92e8c8 100644 --- a/Userland/Libraries/LibFileSystemAccessClient/Client.h +++ b/Userland/Libraries/LibFileSystemAccessClient/Client.h @@ -19,6 +19,7 @@ namespace FileSystemAccessClient { using DeprecatedResult = ErrorOr>; +using Result = ErrorOr>; class Client final : public IPC::ConnectionToServer @@ -31,6 +32,8 @@ public: DeprecatedResult try_open_file(GUI::Window* parent_window, DeprecatedString const& window_title = {}, StringView path = Core::StandardPaths::home_directory(), Core::OpenMode requested_access = Core::OpenMode::ReadOnly); DeprecatedResult try_save_file_deprecated(GUI::Window* parent_window, DeprecatedString const& name, DeprecatedString const ext, Core::OpenMode requested_access = Core::OpenMode::WriteOnly | Core::OpenMode::Truncate); + Result save_file(GUI::Window* parent_window, DeprecatedString const& name, DeprecatedString const ext, Core::Stream::OpenMode requested_access = Core::Stream::OpenMode::Write | Core::Stream::OpenMode::Truncate); + static Client& the(); protected: @@ -45,10 +48,14 @@ private: virtual void handle_prompt_end(i32 request_id, i32 error, Optional const& fd, Optional const& chosen_file) override; int get_new_id(); - DeprecatedResult handle_promise(int); + template + AnyResult handle_promise(int); + + template + using PromiseType = RefPtr>; struct PromiseAndWindow { - RefPtr> promise {}; + Variant, PromiseType> promise; GUI::Window* parent_window { nullptr }; };