From 2e35a065ba65d0d51b6f5742aea8ca43cef80c3d Mon Sep 17 00:00:00 2001 From: Keeley Hammond Date: Wed, 15 Jan 2025 02:33:20 -0800 Subject: [PATCH] fix: revert required portal version for file chooser dialogs (#45193) --- docs/api/command-line-switches.md | 7 + docs/api/dialog.md | 10 ++ ...ing_dialog_features_to_shell_dialogs.patch | 141 ++++++++++++++++-- shell/browser/ui/file_dialog_linux.cc | 16 ++ 4 files changed, 158 insertions(+), 16 deletions(-) diff --git a/docs/api/command-line-switches.md b/docs/api/command-line-switches.md index 67e6ddfd453..6c80fde7c17 100644 --- a/docs/api/command-line-switches.md +++ b/docs/api/command-line-switches.md @@ -241,6 +241,13 @@ Force using discrete GPU when there are multiple GPUs available. Force using integrated GPU when there are multiple GPUs available. +### --xdg-portal-required-version=`version` + +Sets the minimum required version of XDG portal implementation to `version` +in order to use the portal backend for file dialogs on linux. File dialogs +will fallback to using gtk or kde depending on the desktop environment when +the required version is unavailable. Current default is set to `3`. + ## Node.js Flags Electron supports some of the [CLI flags][node-cli] supported by Node.js. diff --git a/docs/api/dialog.md b/docs/api/dialog.md index 78cb384a77c..81458ddece3 100644 --- a/docs/api/dialog.md +++ b/docs/api/dialog.md @@ -78,6 +78,11 @@ dialog.showOpenDialogSync(mainWindow, { }) ``` +**Note:** On Linux `defaultPath` is not supported when using portal file chooser +dialogs unless the portal backend is version 4 or higher. You can use `--xdg-portal-required-version` +[command-line switch](./command-line-switches.md#--xdg-portal-required-versionversion) +to force gtk or kde dialogs. + ### `dialog.showOpenDialog([window, ]options)` * `window` [BaseWindow](base-window.md) (optional) @@ -150,6 +155,11 @@ dialog.showOpenDialog(mainWindow, { }) ``` +**Note:** On Linux `defaultPath` is not supported when using portal file chooser +dialogs unless the portal backend is version 4 or higher. You can use `--xdg-portal-required-version` +[command-line switch](./command-line-switches.md#--xdg-portal-required-versionversion) +to force gtk or kde dialogs. + ### `dialog.showSaveDialogSync([window, ]options)` * `window` [BaseWindow](base-window.md) (optional) diff --git a/patches/chromium/feat_add_support_for_missing_dialog_features_to_shell_dialogs.patch b/patches/chromium/feat_add_support_for_missing_dialog_features_to_shell_dialogs.patch index b4ace489ac1..c8ac5680fe6 100644 --- a/patches/chromium/feat_add_support_for_missing_dialog_features_to_shell_dialogs.patch +++ b/patches/chromium/feat_add_support_for_missing_dialog_features_to_shell_dialogs.patch @@ -199,19 +199,67 @@ index 58985ce62dc569256bad5e94de9c0d125fc470d0..33436784b691c860d58f8b4dfcc6718e &SelectFileDialogLinuxKde::OnSelectSingleFolderDialogResponse, this, parent)); diff --git a/ui/shell_dialogs/select_file_dialog_linux_portal.cc b/ui/shell_dialogs/select_file_dialog_linux_portal.cc -index d94540d0a7bf90f57acdaf8ca6665cf283a646bf..7a99193c3ed192fc80f929dfabdc95d2c308a87e 100644 +index d94540d0a7bf90f57acdaf8ca6665cf283a646bf..e8578b4853d6b82102b29046bdd07c8b80039846 100644 --- a/ui/shell_dialogs/select_file_dialog_linux_portal.cc +++ b/ui/shell_dialogs/select_file_dialog_linux_portal.cc -@@ -39,7 +39,7 @@ constexpr char kMethodStartServiceByName[] = "StartServiceByName"; +@@ -7,6 +7,7 @@ + #include + + #include "base/containers/contains.h" ++#include "base/command_line.h" + #include "base/functional/bind.h" + #include "base/logging.h" + #include "base/no_destructor.h" +@@ -39,6 +40,8 @@ constexpr char kMethodStartServiceByName[] = "StartServiceByName"; constexpr char kXdgPortalService[] = "org.freedesktop.portal.Desktop"; constexpr char kXdgPortalObject[] = "/org/freedesktop/portal/desktop"; --constexpr int kXdgPortalRequiredVersion = 3; -+constexpr int kXdgPortalRequiredVersion = 4; ++// Version 4 includes support for current_folder option to the OpenFile method via ++// https://github.com/flatpak/xdg-desktop-portal/commit/71165a5. + constexpr int kXdgPortalRequiredVersion = 3; constexpr char kXdgPortalRequestInterfaceName[] = - "org.freedesktop.portal.Request"; -@@ -216,6 +216,8 @@ void SelectFileDialogLinuxPortal::SelectFileImpl( +@@ -67,6 +70,7 @@ constexpr char kFileUriPrefix[] = "file://"; + + // Time to wait for the notification service to start, in milliseconds. + constexpr base::TimeDelta kStartServiceTimeout = base::Seconds(1); ++const char kXdgPortalRequiredVersionFlag[] = "xdg-portal-required-version"; + + struct FileChooserProperties : dbus::PropertySet { + dbus::Property version; +@@ -173,10 +177,18 @@ void SelectFileDialogLinuxPortal::StartAvailabilityTestInBackground() { + if (GetAvailabilityTestCompletionFlag()->IsSet()) + return; + ++ auto* cmd = base::CommandLine::ForCurrentProcess(); ++ unsigned int xdg_portal_required_version; ++ if (!base::StringToUint(cmd->GetSwitchValueASCII(kXdgPortalRequiredVersionFlag), ++ &xdg_portal_required_version)) { ++ xdg_portal_required_version = kXdgPortalRequiredVersion; ++ } ++ + dbus_thread_linux::GetTaskRunner()->PostTask( + FROM_HERE, + base::BindOnce( +- &SelectFileDialogLinuxPortal::CheckPortalAvailabilityOnBusThread)); ++ &SelectFileDialogLinuxPortal::CheckPortalAvailabilityOnBusThread, ++ xdg_portal_required_version)); + } + + // static +@@ -187,6 +199,11 @@ bool SelectFileDialogLinuxPortal::IsPortalAvailable() { + return is_portal_available_; + } + ++// static ++int SelectFileDialogLinuxPortal::GetPortalVersion() { ++ return available_portal_version_; ++} ++ + // static + void SelectFileDialogLinuxPortal::DestroyPortalConnection() { + dbus_thread_linux::GetTaskRunner()->PostTask( +@@ -216,6 +233,8 @@ void SelectFileDialogLinuxPortal::SelectFileImpl( weak_factory_.GetWeakPtr())); info_->type = type; info_->main_task_runner = base::SequencedTaskRunner::GetCurrentDefault(); @@ -220,7 +268,37 @@ index d94540d0a7bf90f57acdaf8ca6665cf283a646bf..7a99193c3ed192fc80f929dfabdc95d2 if (owning_window) { if (auto* root = owning_window->GetRootWindow()) { -@@ -552,7 +554,9 @@ void SelectFileDialogLinuxPortal::DialogInfo::AppendOptions( +@@ -262,7 +281,8 @@ bool SelectFileDialogLinuxPortal::HasMultipleFileTypeChoicesImpl() { + } + + // static +-void SelectFileDialogLinuxPortal::CheckPortalAvailabilityOnBusThread() { ++void SelectFileDialogLinuxPortal::CheckPortalAvailabilityOnBusThread( ++ unsigned int xdg_portal_required_version) { + DCHECK(dbus_thread_linux::GetTaskRunner()->RunsTasksInCurrentSequence()); + base::AtomicFlag* availability_test_complete = + GetAvailabilityTestCompletionFlag(); +@@ -283,11 +303,18 @@ void SelectFileDialogLinuxPortal::CheckPortalAvailabilityOnBusThread() { + FileChooserProperties properties(portal); + if (!properties.GetAndBlock(&properties.version)) { + LOG(ERROR) << "Failed to read portal version property"; +- } else if (properties.version.value() >= kXdgPortalRequiredVersion) { ++ } else if (properties.version.value() >= xdg_portal_required_version) { + is_portal_available_ = true; ++ available_portal_version_ = properties.version.value(); ++ } else { ++ VLOG(1) << "File chooser portal available version: " ++ << properties.version.value(); ++ available_portal_version_ = properties.version.value(); + } + } + ++ VLOG(1) << "File chooser portal expected version: " ++ << xdg_portal_required_version; + VLOG(1) << "File chooser portal available: " + << (is_portal_available_ ? "yes" : "no"); + availability_test_complete->Set(); +@@ -552,7 +579,9 @@ void SelectFileDialogLinuxPortal::DialogInfo::AppendOptions( response_handle_token); if (type == SelectFileDialog::Type::SELECT_UPLOAD_FOLDER) { @@ -231,7 +309,7 @@ index d94540d0a7bf90f57acdaf8ca6665cf283a646bf..7a99193c3ed192fc80f929dfabdc95d2 l10n_util::GetStringUTF8( IDS_SELECT_UPLOAD_FOLDER_DIALOG_UPLOAD_BUTTON)); } -@@ -561,12 +565,13 @@ void SelectFileDialogLinuxPortal::DialogInfo::AppendOptions( +@@ -561,6 +590,8 @@ void SelectFileDialogLinuxPortal::DialogInfo::AppendOptions( type == SelectFileDialog::Type::SELECT_UPLOAD_FOLDER || type == SelectFileDialog::Type::SELECT_EXISTING_FOLDER) { AppendBoolOption(&options_writer, kFileChooserOptionDirectory, true); @@ -240,18 +318,29 @@ index d94540d0a7bf90f57acdaf8ca6665cf283a646bf..7a99193c3ed192fc80f929dfabdc95d2 } else if (type == SelectFileDialog::Type::SELECT_OPEN_MULTI_FILE) { AppendBoolOption(&options_writer, kFileChooserOptionMultiple, true); } +@@ -874,6 +905,7 @@ SelectFileDialogLinuxPortal::DialogInfo::ConvertUrisToPaths( + } -- if (type == SelectFileDialog::Type::SELECT_SAVEAS_FILE && -- !default_path.empty()) { -+ if (!default_path.empty()) { - if (default_path_exists) { - // If this is an existing directory, navigate to that directory, with no - // filename. + bool SelectFileDialogLinuxPortal::is_portal_available_ = false; ++unsigned int SelectFileDialogLinuxPortal::available_portal_version_ = 0; + int SelectFileDialogLinuxPortal::handle_token_counter_ = 0; + + } // namespace ui diff --git a/ui/shell_dialogs/select_file_dialog_linux_portal.h b/ui/shell_dialogs/select_file_dialog_linux_portal.h -index 47e3b0e658858ba5f3219f04d258bdf6dd7c26ed..ff8eaabb406cdf759f7a62725171aaf9f74ce183 100644 +index 47e3b0e658858ba5f3219f04d258bdf6dd7c26ed..7124f65d801b086cf39f3640027b3deeca9b6dca 100644 --- a/ui/shell_dialogs/select_file_dialog_linux_portal.h +++ b/ui/shell_dialogs/select_file_dialog_linux_portal.h -@@ -117,6 +117,8 @@ class SelectFileDialogLinuxPortal : public SelectFileDialogLinux { +@@ -44,6 +44,9 @@ class SelectFileDialogLinuxPortal : public SelectFileDialogLinux { + // test from above has not yet completed (which should generally not happen). + static bool IsPortalAvailable(); + ++ // Get version of portal if available. ++ static int GetPortalVersion(); ++ + // Destroys the connection to the bus. + static void DestroyPortalConnection(); + +@@ -117,6 +120,8 @@ class SelectFileDialogLinuxPortal : public SelectFileDialogLinux { Type type; // The task runner the SelectFileImpl method was called on. scoped_refptr main_task_runner; @@ -260,3 +349,23 @@ index 47e3b0e658858ba5f3219f04d258bdf6dd7c26ed..ff8eaabb406cdf759f7a62725171aaf9 private: friend class base::RefCountedThreadSafe; +@@ -173,7 +178,8 @@ class SelectFileDialogLinuxPortal : public SelectFileDialogLinux { + }; + + // D-Bus configuration and initialization. +- static void CheckPortalAvailabilityOnBusThread(); ++ static void CheckPortalAvailabilityOnBusThread( ++ unsigned int xdg_portal_required_version); + static bool IsPortalRunningOnBusThread(dbus::ObjectProxy* dbus_proxy); + static bool IsPortalActivatableOnBusThread(dbus::ObjectProxy* dbus_proxy); + +@@ -207,6 +213,9 @@ class SelectFileDialogLinuxPortal : public SelectFileDialogLinux { + // Written by the D-Bus thread and read by the UI thread. + static bool is_portal_available_; + ++ // Written by the D-Bus thread and read by the UI thread. ++ static unsigned int available_portal_version_; ++ + // Used by the D-Bus thread to generate unique handle tokens. + static int handle_token_counter_; + diff --git a/shell/browser/ui/file_dialog_linux.cc b/shell/browser/ui/file_dialog_linux.cc index a46cb6b886b..050460c4e42 100644 --- a/shell/browser/ui/file_dialog_linux.cc +++ b/shell/browser/ui/file_dialog_linux.cc @@ -20,6 +20,8 @@ #include "shell/common/gin_converters/file_path_converter.h" #include "ui/gtk/select_file_dialog_linux_gtk.h" // nogncheck #include "ui/shell_dialogs/select_file_dialog.h" +#include "ui/shell_dialogs/select_file_dialog_linux_portal.h" +#include "ui/shell_dialogs/select_file_policy.h" #include "ui/shell_dialogs/selected_file_info.h" namespace file_dialog { @@ -59,6 +61,18 @@ ui::SelectFileDialog::FileTypeInfo GetFilterInfo(const Filters& filters) { return file_type_info; } +void LogIfNeededAboutUnsupportedPortalFeature(const DialogSettings& settings) { + if (!settings.default_path.empty() && + ui::SelectFileDialogLinuxPortal::IsPortalAvailable() && + ui::SelectFileDialogLinuxPortal::GetPortalVersion() < 4) { + LOG(INFO) << "Available portal version " + << ui::SelectFileDialogLinuxPortal::GetPortalVersion() + << " does not support defaultPath option, try the non-portal" + << " file chooser dialogs by launching with" + << " --xdg-portal-required-version"; + } +} + class FileChooserDialog : public ui::SelectFileDialog::Listener { public: enum class DialogType { OPEN, SAVE }; @@ -201,6 +215,7 @@ class FileChooserDialog : public ui::SelectFileDialog::Listener { bool ShowOpenDialogSync(const DialogSettings& settings, std::vector* paths) { + LogIfNeededAboutUnsupportedPortalFeature(settings); base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed); auto cb = base::BindOnce( [](base::RepeatingClosure cb, std::vector* file_paths, @@ -218,6 +233,7 @@ bool ShowOpenDialogSync(const DialogSettings& settings, void ShowOpenDialog(const DialogSettings& settings, gin_helper::Promise promise) { + LogIfNeededAboutUnsupportedPortalFeature(settings); FileChooserDialog* dialog = new FileChooserDialog(); dialog->RunOpenDialog(std::move(promise), settings); }