From 4fb5aab2ef1e928079dbc340f942afd54920a8cc Mon Sep 17 00:00:00 2001 From: Robo Date: Fri, 15 Nov 2024 23:31:33 +0900 Subject: [PATCH] fix: revert required portal version for file chooser dialogs (#44426) * feat: add support for configuring xdg portal version at runtime * doc: update command-line-switches.md * doc: update command-line-switches.md Co-authored-by: John Kleinschmidt * doc: required portal version for defaultPath support Co-authored-by: John Kleinschmidt * doc: update more occurrances * fix: remove warning from save dialogs * doc: update command-line-switches.md Co-authored-by: John Kleinschmidt --------- Co-authored-by: John Kleinschmidt --- docs/api/command-line-switches.md | 7 + docs/api/dialog.md | 10 ++ ...ing_dialog_features_to_shell_dialogs.patch | 148 ++++++++++++++++-- shell/browser/ui/file_dialog_linux.cc | 15 ++ 4 files changed, 170 insertions(+), 10 deletions(-) diff --git a/docs/api/command-line-switches.md b/docs/api/command-line-switches.md index 67e6ddfd4530..6c80fde7c179 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 78cb384a77c3..81458ddece3b 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 7211ffe10093..e8582082356f 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,21 +199,68 @@ index 64a79ebe2e2d21d5a6b4a98042d1cdb7b6edad52..16f2ae01a8d33e6341ed52638e963c34 &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 143f5fe1028e154192767599a1e68b45301a894d..d612e1614a313db0dcf7dc592fd6fa74c89e70e1 100644 +index 143f5fe1028e154192767599a1e68b45301a894d..132e670dc3ccd9a0f904a8869e516f4556fbf0af 100644 --- a/ui/shell_dialogs/select_file_dialog_linux_portal.cc +++ b/ui/shell_dialogs/select_file_dialog_linux_portal.cc -@@ -40,7 +40,9 @@ namespace { +@@ -11,6 +11,7 @@ + + #include + ++#include "base/command_line.h" + #include "base/functional/bind.h" + #include "base/logging.h" + #include "base/no_destructor.h" +@@ -40,6 +41,8 @@ namespace { constexpr char kXdgPortalService[] = "org.freedesktop.portal.Desktop"; constexpr char kXdgPortalObject[] = "/org/freedesktop/portal/desktop"; --constexpr int kXdgPortalRequiredVersion = 3; +// 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 = 4; + constexpr int kXdgPortalRequiredVersion = 3; constexpr char kXdgPortalRequestInterfaceName[] = - "org.freedesktop.portal.Request"; -@@ -214,6 +216,8 @@ void SelectFileDialogLinuxPortal::SelectFileImpl( +@@ -66,6 +69,8 @@ constexpr int kFileChooserFilterKindGlob = 0; + + constexpr char kFileUriPrefix[] = "file://"; + ++const char kXdgPortalRequiredVersionFlag[] = "xdg-portal-required-version"; ++ + struct FileChooserProperties : dbus::PropertySet { + dbus::Property version; + +@@ -171,10 +176,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 +@@ -185,6 +198,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( +@@ -214,6 +232,8 @@ void SelectFileDialogLinuxPortal::SelectFileImpl( weak_factory_.GetWeakPtr())); info_->type = type; info_->main_task_runner = base::SequencedTaskRunner::GetCurrentDefault(); @@ -222,7 +269,50 @@ index 143f5fe1028e154192767599a1e68b45301a894d..d612e1614a313db0dcf7dc592fd6fa74 if (owning_window) { if (auto* root = owning_window->GetRootWindow()) { -@@ -471,7 +475,9 @@ void SelectFileDialogLinuxPortal::DialogInfo::AppendOptions( +@@ -260,7 +280,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(); +@@ -274,6 +295,7 @@ void SelectFileDialogLinuxPortal::CheckPortalAvailabilityOnBusThread() { + base::BindOnce( + [](scoped_refptr bus, + base::AtomicFlag* availability_test_complete, ++ unsigned int xdg_portal_required_version, + std::optional name_has_owner) { + if (name_has_owner.value_or(false)) { + // The portal service has an owner, proceed to check the version. +@@ -285,15 +307,22 @@ void SelectFileDialogLinuxPortal::CheckPortalAvailabilityOnBusThread() { + if (!properties.GetAndBlock(&properties.version)) { + LOG(ERROR) << "Failed to read portal version property"; + } else if (properties.version.value() >= +- kXdgPortalRequiredVersion) { ++ 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(); + }, +- bus, availability_test_complete)); ++ bus, availability_test_complete, xdg_portal_required_version)); + } + + // static +@@ -471,7 +500,9 @@ void SelectFileDialogLinuxPortal::DialogInfo::AppendOptions( response_handle_token); if (type == SelectFileDialog::Type::SELECT_UPLOAD_FOLDER) { @@ -233,7 +323,7 @@ index 143f5fe1028e154192767599a1e68b45301a894d..d612e1614a313db0dcf7dc592fd6fa74 l10n_util::GetStringUTF8( IDS_SELECT_UPLOAD_FOLDER_DIALOG_UPLOAD_BUTTON)); } -@@ -480,6 +486,8 @@ void SelectFileDialogLinuxPortal::DialogInfo::AppendOptions( +@@ -480,6 +511,8 @@ void SelectFileDialogLinuxPortal::DialogInfo::AppendOptions( type == SelectFileDialog::Type::SELECT_UPLOAD_FOLDER || type == SelectFileDialog::Type::SELECT_EXISTING_FOLDER) { AppendBoolOption(&options_writer, kFileChooserOptionDirectory, true); @@ -242,11 +332,29 @@ index 143f5fe1028e154192767599a1e68b45301a894d..d612e1614a313db0dcf7dc592fd6fa74 } else if (type == SelectFileDialog::Type::SELECT_OPEN_MULTI_FILE) { AppendBoolOption(&options_writer, kFileChooserOptionMultiple, true); } +@@ -820,6 +853,7 @@ SelectFileDialogLinuxPortal::DialogInfo::DialogInfo( + SelectFileDialogLinuxPortal::DialogInfo::~DialogInfo() = default; + + 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 d57a52b3ccbd3bd6d390615351ea2ad1e475b157..433f34ac6779611623241cd977dd1214e97fece7 100644 +index d57a52b3ccbd3bd6d390615351ea2ad1e475b157..6a2800add2428ffd91286748f886d6c42510ba31 100644 --- a/ui/shell_dialogs/select_file_dialog_linux_portal.h +++ b/ui/shell_dialogs/select_file_dialog_linux_portal.h -@@ -120,6 +120,8 @@ class SelectFileDialogLinuxPortal : public SelectFileDialogLinux { +@@ -47,6 +47,9 @@ class SelectFileDialogLinuxPortal : public SelectFileDialogLinux { + // availability test has not yet completed. + static bool IsPortalAvailable(); + ++ // Get version of portal if available. ++ static int GetPortalVersion(); ++ + // Destroys the connection to the bus. + static void DestroyPortalConnection(); + +@@ -120,6 +123,8 @@ class SelectFileDialogLinuxPortal : public SelectFileDialogLinux { Type type; // The task runner the SelectFileImpl method was called on. scoped_refptr main_task_runner; @@ -255,3 +363,23 @@ index d57a52b3ccbd3bd6d390615351ea2ad1e475b157..433f34ac6779611623241cd977dd1214 private: friend class base::RefCountedThreadSafe; +@@ -176,7 +181,8 @@ class SelectFileDialogLinuxPortal : public SelectFileDialogLinux { + }; + + // D-Bus configuration and initialization. +- static void CheckPortalAvailabilityOnBusThread(); ++ static void CheckPortalAvailabilityOnBusThread( ++ unsigned int xdg_portal_required_version); + + // Returns a flag, written by the D-Bus thread and read by the UI thread, + // indicating whether or not the availability test has completed. +@@ -208,6 +214,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 befbac20fe9b..ad6524877830 100644 --- a/shell/browser/ui/file_dialog_linux.cc +++ b/shell/browser/ui/file_dialog_linux.cc @@ -18,6 +18,7 @@ #include "shell/common/gin_helper/dictionary.h" #include "shell/common/gin_helper/promise.h" #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" @@ -58,6 +59,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 }; @@ -200,6 +213,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, @@ -217,6 +231,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); }