fix: revert required portal version for file chooser dialogs (#45193)

This commit is contained in:
Keeley Hammond 2025-01-15 02:33:20 -08:00 committed by GitHub
parent 9dccc9f4b4
commit 2e35a065ba
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 158 additions and 16 deletions

View file

@ -241,6 +241,13 @@ Force using discrete GPU when there are multiple GPUs available.
Force using integrated 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 ## Node.js Flags
Electron supports some of the [CLI flags][node-cli] supported by Node.js. Electron supports some of the [CLI flags][node-cli] supported by Node.js.

View file

@ -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)` ### `dialog.showOpenDialog([window, ]options)`
* `window` [BaseWindow](base-window.md) (optional) * `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)` ### `dialog.showSaveDialogSync([window, ]options)`
* `window` [BaseWindow](base-window.md) (optional) * `window` [BaseWindow](base-window.md) (optional)

View file

@ -199,19 +199,67 @@ index 58985ce62dc569256bad5e94de9c0d125fc470d0..33436784b691c860d58f8b4dfcc6718e
&SelectFileDialogLinuxKde::OnSelectSingleFolderDialogResponse, this, &SelectFileDialogLinuxKde::OnSelectSingleFolderDialogResponse, this,
parent)); parent));
diff --git a/ui/shell_dialogs/select_file_dialog_linux_portal.cc b/ui/shell_dialogs/select_file_dialog_linux_portal.cc 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 --- a/ui/shell_dialogs/select_file_dialog_linux_portal.cc
+++ b/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 <string_view>
#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 kXdgPortalService[] = "org.freedesktop.portal.Desktop";
constexpr char kXdgPortalObject[] = "/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
+constexpr int kXdgPortalRequiredVersion = 4; +// https://github.com/flatpak/xdg-desktop-portal/commit/71165a5.
constexpr int kXdgPortalRequiredVersion = 3;
constexpr char kXdgPortalRequestInterfaceName[] = constexpr char kXdgPortalRequestInterfaceName[] =
"org.freedesktop.portal.Request"; @@ -67,6 +70,7 @@ constexpr char kFileUriPrefix[] = "file://";
@@ -216,6 +216,8 @@ void SelectFileDialogLinuxPortal::SelectFileImpl(
// 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<uint32_t> 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())); weak_factory_.GetWeakPtr()));
info_->type = type; info_->type = type;
info_->main_task_runner = base::SequencedTaskRunner::GetCurrentDefault(); info_->main_task_runner = base::SequencedTaskRunner::GetCurrentDefault();
@ -220,7 +268,37 @@ index d94540d0a7bf90f57acdaf8ca6665cf283a646bf..7a99193c3ed192fc80f929dfabdc95d2
if (owning_window) { if (owning_window) {
if (auto* root = owning_window->GetRootWindow()) { 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); response_handle_token);
if (type == SelectFileDialog::Type::SELECT_UPLOAD_FOLDER) { if (type == SelectFileDialog::Type::SELECT_UPLOAD_FOLDER) {
@ -231,7 +309,7 @@ index d94540d0a7bf90f57acdaf8ca6665cf283a646bf..7a99193c3ed192fc80f929dfabdc95d2
l10n_util::GetStringUTF8( l10n_util::GetStringUTF8(
IDS_SELECT_UPLOAD_FOLDER_DIALOG_UPLOAD_BUTTON)); 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_UPLOAD_FOLDER ||
type == SelectFileDialog::Type::SELECT_EXISTING_FOLDER) { type == SelectFileDialog::Type::SELECT_EXISTING_FOLDER) {
AppendBoolOption(&options_writer, kFileChooserOptionDirectory, true); AppendBoolOption(&options_writer, kFileChooserOptionDirectory, true);
@ -240,18 +318,29 @@ index d94540d0a7bf90f57acdaf8ca6665cf283a646bf..7a99193c3ed192fc80f929dfabdc95d2
} else if (type == SelectFileDialog::Type::SELECT_OPEN_MULTI_FILE) { } else if (type == SelectFileDialog::Type::SELECT_OPEN_MULTI_FILE) {
AppendBoolOption(&options_writer, kFileChooserOptionMultiple, true); AppendBoolOption(&options_writer, kFileChooserOptionMultiple, true);
} }
@@ -874,6 +905,7 @@ SelectFileDialogLinuxPortal::DialogInfo::ConvertUrisToPaths(
}
- if (type == SelectFileDialog::Type::SELECT_SAVEAS_FILE && bool SelectFileDialogLinuxPortal::is_portal_available_ = false;
- !default_path.empty()) { +unsigned int SelectFileDialogLinuxPortal::available_portal_version_ = 0;
+ if (!default_path.empty()) { int SelectFileDialogLinuxPortal::handle_token_counter_ = 0;
if (default_path_exists) {
// If this is an existing directory, navigate to that directory, with no } // namespace ui
// filename.
diff --git a/ui/shell_dialogs/select_file_dialog_linux_portal.h b/ui/shell_dialogs/select_file_dialog_linux_portal.h 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 --- a/ui/shell_dialogs/select_file_dialog_linux_portal.h
+++ b/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; Type type;
// The task runner the SelectFileImpl method was called on. // The task runner the SelectFileImpl method was called on.
scoped_refptr<base::SequencedTaskRunner> main_task_runner; scoped_refptr<base::SequencedTaskRunner> main_task_runner;
@ -260,3 +349,23 @@ index 47e3b0e658858ba5f3219f04d258bdf6dd7c26ed..ff8eaabb406cdf759f7a62725171aaf9
private: private:
friend class base::RefCountedThreadSafe<DialogInfo>; friend class base::RefCountedThreadSafe<DialogInfo>;
@@ -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_;

View file

@ -20,6 +20,8 @@
#include "shell/common/gin_converters/file_path_converter.h" #include "shell/common/gin_converters/file_path_converter.h"
#include "ui/gtk/select_file_dialog_linux_gtk.h" // nogncheck #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.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" #include "ui/shell_dialogs/selected_file_info.h"
namespace file_dialog { namespace file_dialog {
@ -59,6 +61,18 @@ ui::SelectFileDialog::FileTypeInfo GetFilterInfo(const Filters& filters) {
return file_type_info; 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 { class FileChooserDialog : public ui::SelectFileDialog::Listener {
public: public:
enum class DialogType { OPEN, SAVE }; enum class DialogType { OPEN, SAVE };
@ -201,6 +215,7 @@ class FileChooserDialog : public ui::SelectFileDialog::Listener {
bool ShowOpenDialogSync(const DialogSettings& settings, bool ShowOpenDialogSync(const DialogSettings& settings,
std::vector<base::FilePath>* paths) { std::vector<base::FilePath>* paths) {
LogIfNeededAboutUnsupportedPortalFeature(settings);
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed); base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
auto cb = base::BindOnce( auto cb = base::BindOnce(
[](base::RepeatingClosure cb, std::vector<base::FilePath>* file_paths, [](base::RepeatingClosure cb, std::vector<base::FilePath>* file_paths,
@ -218,6 +233,7 @@ bool ShowOpenDialogSync(const DialogSettings& settings,
void ShowOpenDialog(const DialogSettings& settings, void ShowOpenDialog(const DialogSettings& settings,
gin_helper::Promise<gin_helper::Dictionary> promise) { gin_helper::Promise<gin_helper::Dictionary> promise) {
LogIfNeededAboutUnsupportedPortalFeature(settings);
FileChooserDialog* dialog = new FileChooserDialog(); FileChooserDialog* dialog = new FileChooserDialog();
dialog->RunOpenDialog(std::move(promise), settings); dialog->RunOpenDialog(std::move(promise), settings);
} }