summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Userland/Applications/TextEditor/CMakeLists.txt2
-rw-r--r--Userland/Applications/TextEditor/MainWidget.cpp42
-rw-r--r--Userland/Applications/TextEditor/MainWidget.h25
-rw-r--r--Userland/Applications/TextEditor/main.cpp48
-rw-r--r--Userland/Libraries/LibGUI/TextEditor.cpp7
-rw-r--r--Userland/Libraries/LibGUI/TextEditor.h3
6 files changed, 109 insertions, 18 deletions
diff --git a/Userland/Applications/TextEditor/CMakeLists.txt b/Userland/Applications/TextEditor/CMakeLists.txt
index b68b8b5b15..a586210eae 100644
--- a/Userland/Applications/TextEditor/CMakeLists.txt
+++ b/Userland/Applications/TextEditor/CMakeLists.txt
@@ -2,7 +2,7 @@ serenity_component(
TextEditor
RECOMMENDED
TARGETS TextEditor
- DEPENDS ImageDecoder RequestServer WebContent
+ DEPENDS ImageDecoder RequestServer WebContent FileSystemAccessServer
)
compile_gml(TextEditorWindow.gml TextEditorWindowGML.h text_editor_window_gml)
diff --git a/Userland/Applications/TextEditor/MainWidget.cpp b/Userland/Applications/TextEditor/MainWidget.cpp
index 77b40b9cab..15728b0199 100644
--- a/Userland/Applications/TextEditor/MainWidget.cpp
+++ b/Userland/Applications/TextEditor/MainWidget.cpp
@@ -44,6 +44,7 @@
namespace TextEditor {
MainWidget::MainWidget()
+ : m_file_system_access_client(FileSystemAccessClient::construct())
{
load_from_gml(text_editor_window_gml);
@@ -259,9 +260,9 @@ MainWidget::MainWidget()
});
m_open_action = GUI::CommonActions::make_open_action([this](auto&) {
- Optional<String> open_path = GUI::FilePicker::get_open_filepath(window());
+ auto fd_response = m_file_system_access_client->prompt_open_file(Core::StandardPaths::home_directory(), Core::OpenMode::ReadWrite);
- if (!open_path.has_value())
+ if (fd_response.error() != 0)
return;
if (editor().document().is_modified()) {
@@ -272,28 +273,36 @@ MainWidget::MainWidget()
return;
}
- open_file(open_path.value());
+ read_file_and_close(fd_response.fd()->take_fd(), fd_response.chosen_file().value());
});
m_save_as_action = GUI::CommonActions::make_save_as_action([&](auto&) {
- Optional<String> save_path = GUI::FilePicker::get_save_filepath(window(), m_name.is_null() ? "Untitled" : m_name, m_extension.is_null() ? "txt" : m_extension);
- if (!save_path.has_value())
+ auto response = m_file_system_access_client->prompt_save_file(m_name.is_null() ? "Untitled" : m_name, m_extension.is_null() ? "txt" : m_extension, Core::StandardPaths::home_directory(), Core::OpenMode::Truncate | Core::OpenMode::WriteOnly);
+
+ if (response.error() != 0)
return;
- if (!m_editor->write_to_file(save_path.value())) {
+ if (!m_editor->write_to_file_and_close(response.fd()->take_fd())) {
GUI::MessageBox::show(window(), "Unable to save file.\n", "Error", GUI::MessageBox::Type::Error);
return;
}
// FIXME: It would be cool if this would propagate from GUI::TextDocument somehow.
window()->set_modified(false);
- set_path(save_path.value());
- dbgln("Wrote document to {}", save_path.value());
+ set_path(response.chosen_file().value());
+ dbgln("Wrote document to {}", response.chosen_file().value());
});
m_save_action = GUI::CommonActions::make_save_action([&](auto&) {
if (!m_path.is_empty()) {
- if (!m_editor->write_to_file(m_path)) {
+ auto response = m_file_system_access_client->request_file(m_path, Core::OpenMode::Truncate | Core::OpenMode::WriteOnly);
+
+ if (response.error() != 0)
+ return;
+
+ int fd = response.fd()->take_fd();
+
+ if (!m_editor->write_to_file_and_close(fd)) {
GUI::MessageBox::show(window(), "Unable to save file.\n", "Error", GUI::MessageBox::Type::Error);
} else {
// FIXME: It would be cool if this would propagate from GUI::TextDocument somehow.
@@ -647,10 +656,12 @@ void MainWidget::update_title()
window()->set_title(builder.to_string());
}
-bool MainWidget::open_file(const String& path)
+bool MainWidget::read_file_and_close(int fd, String const& path)
{
- auto file = Core::File::construct(path);
- if (!file->open(Core::OpenMode::ReadOnly) && file->error() != ENOENT) {
+ VERIFY(path.starts_with("/"sv));
+ auto file = Core::File::construct();
+
+ if (!file->open(fd, Core::OpenMode::ReadOnly, Core::File::ShouldCloseFileDescriptor::Yes) && file->error() != ENOENT) {
GUI::MessageBox::show(window(), String::formatted("Opening \"{}\" failed: {}", path, strerror(errno)), "Error", GUI::MessageBox::Type::Error);
return false;
}
@@ -706,7 +717,12 @@ void MainWidget::drop_event(GUI::DropEvent& event)
GUI::MessageBox::show(window(), "TextEditor can only open one file at a time!", "One at a time please!", GUI::MessageBox::Type::Error);
return;
}
- open_file(urls.first().path());
+ auto file_response = m_file_system_access_client->request_file(urls.first().path(), Core::OpenMode::ReadOnly);
+
+ if (file_response.error() != 0)
+ return;
+
+ read_file_and_close(file_response.fd()->take_fd(), urls.first().path());
}
}
diff --git a/Userland/Applications/TextEditor/MainWidget.h b/Userland/Applications/TextEditor/MainWidget.h
index 4cccac8181..190a206349 100644
--- a/Userland/Applications/TextEditor/MainWidget.h
+++ b/Userland/Applications/TextEditor/MainWidget.h
@@ -8,22 +8,43 @@
#include <AK/Function.h>
#include <AK/LexicalPath.h>
+#include <FileSystemAccessServer/ClientConnection.h>
+#include <FileSystemAccessServer/FileSystemAccessClientEndpoint.h>
+#include <FileSystemAccessServer/FileSystemAccessServerEndpoint.h>
#include <LibGUI/ActionGroup.h>
#include <LibGUI/Application.h>
#include <LibGUI/Icon.h>
#include <LibGUI/TextEditor.h>
#include <LibGUI/Widget.h>
#include <LibGUI/Window.h>
+#include <LibIPC/ServerConnection.h>
#include <LibWeb/Forward.h>
namespace TextEditor {
+class FileSystemAccessClient final
+ : public IPC::ServerConnection<FileSystemAccessClientEndpoint, FileSystemAccessServerEndpoint>
+ , public FileSystemAccessClientEndpoint {
+ C_OBJECT(FileSystemAccessClient)
+
+public:
+ virtual void die() override
+ {
+ }
+
+private:
+ explicit FileSystemAccessClient()
+ : IPC::ServerConnection<FileSystemAccessClientEndpoint, FileSystemAccessServerEndpoint>(*this, "/tmp/portal/filesystemaccess")
+ {
+ }
+};
+
class MainWidget final : public GUI::Widget {
C_OBJECT(MainWidget);
public:
virtual ~MainWidget() override;
- bool open_file(const String& path);
+ bool read_file_and_close(int fd, String const& path);
bool request_close();
GUI::TextEditor& editor() { return *m_editor; }
@@ -53,6 +74,8 @@ private:
virtual void drop_event(GUI::DropEvent&) override;
+ NonnullRefPtr<FileSystemAccessClient> m_file_system_access_client;
+
RefPtr<GUI::TextEditor> m_editor;
String m_path;
String m_name;
diff --git a/Userland/Applications/TextEditor/main.cpp b/Userland/Applications/TextEditor/main.cpp
index ad1a56b313..a79cb0b6d5 100644
--- a/Userland/Applications/TextEditor/main.cpp
+++ b/Userland/Applications/TextEditor/main.cpp
@@ -7,6 +7,8 @@
#include "FileArgument.h"
#include "MainWidget.h"
#include <LibCore/ArgsParser.h>
+#include <LibCore/File.h>
+#include <LibCore/StandardPaths.h>
#include <LibGUI/Menubar.h>
#include <stdio.h>
#include <unistd.h>
@@ -30,6 +32,43 @@ int main(int argc, char** argv)
parser.parse(argc, argv);
+ if (file_to_edit) {
+ FileArgument parsed_argument(file_to_edit);
+
+ auto path_to_unveil = Core::File::real_path_for(parsed_argument.filename());
+ if (unveil(path_to_unveil.characters(), "rwc") < 0) {
+ perror("unveil");
+ return 1;
+ }
+ }
+
+ if (unveil(Core::StandardPaths::config_directory().characters(), "rw") < 0) {
+ perror("unveil");
+ return 1;
+ }
+
+ if (unveil("/res", "r") < 0) {
+ perror("unveil");
+ return 1;
+ }
+
+ if (unveil("/tmp/portal/launch", "rw") < 0) {
+ perror("unveil");
+ return 1;
+ }
+
+ if (unveil("/tmp/portal/webcontent", "rw") < 0) {
+ perror("unveil");
+ return 1;
+ }
+
+ if (unveil("/tmp/portal/filesystemaccess", "rw") < 0) {
+ perror("unveil");
+ return 1;
+ }
+
+ unveil(nullptr, nullptr);
+
StringView preview_mode_view = preview_mode;
auto app_icon = GUI::Icon::default_icon("app-text-editor");
@@ -67,8 +106,15 @@ int main(int argc, char** argv)
if (file_to_edit) {
// A file name was passed, parse any possible line and column numbers included.
FileArgument parsed_argument(file_to_edit);
- if (!text_widget.open_file(parsed_argument.filename()))
+ auto absolute_path = Core::File::real_path_for(parsed_argument.filename());
+ auto file = Core::File::open(absolute_path, Core::OpenMode::ReadWrite);
+
+ if (file.is_error())
return 1;
+
+ if (!text_widget.read_file_and_close(file.value()->leak_fd(), absolute_path))
+ return 1;
+
text_widget.editor().set_cursor_and_focus_line(parsed_argument.line().value_or(1) - 1, parsed_argument.column().value_or(0));
}
text_widget.update_title();
diff --git a/Userland/Libraries/LibGUI/TextEditor.cpp b/Userland/Libraries/LibGUI/TextEditor.cpp
index 3a9617d0e8..bf7e18fa4d 100644
--- a/Userland/Libraries/LibGUI/TextEditor.cpp
+++ b/Userland/Libraries/LibGUI/TextEditor.cpp
@@ -1198,7 +1198,7 @@ void TextEditor::timer_event(Core::TimerEvent&)
update_cursor();
}
-bool TextEditor::write_to_file(const String& path)
+bool TextEditor::write_to_file(String const& path)
{
int fd = open(path.characters(), O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (fd < 0) {
@@ -1206,6 +1206,11 @@ bool TextEditor::write_to_file(const String& path)
return false;
}
+ return write_to_file_and_close(fd);
+}
+
+bool TextEditor::write_to_file_and_close(int fd)
+{
ScopeGuard fd_guard = [fd] { close(fd); };
off_t file_size = 0;
diff --git a/Userland/Libraries/LibGUI/TextEditor.h b/Userland/Libraries/LibGUI/TextEditor.h
index ecbbf53eae..a287c0c6b1 100644
--- a/Userland/Libraries/LibGUI/TextEditor.h
+++ b/Userland/Libraries/LibGUI/TextEditor.h
@@ -116,7 +116,8 @@ public:
TextRange normalized_selection() const { return m_selection.normalized(); }
void insert_at_cursor_or_replace_selection(const StringView&);
- bool write_to_file(const String& path);
+ bool write_to_file(String const& path);
+ bool write_to_file_and_close(int fd);
bool has_selection() const { return m_selection.is_valid(); }
String selected_text() const;
size_t number_of_selected_words() const;