Skip to content

Commit

Permalink
fix: revert required portal version for file chooser dialogs (electro…
Browse files Browse the repository at this point in the history
…n#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 <[email protected]>

* doc: required portal version for defaultPath support

Co-authored-by: John Kleinschmidt <[email protected]>

* doc: update more occurrances

* fix: remove warning from save dialogs

* doc: update command-line-switches.md

Co-authored-by: John Kleinschmidt <[email protected]>

---------

Co-authored-by: John Kleinschmidt <[email protected]>
  • Loading branch information
deepak1556 and jkleinsc authored Nov 15, 2024
1 parent 27fe6cc commit 4fb5aab
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 10 deletions.
7 changes: 7 additions & 0 deletions docs/api/command-line-switches.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
10 changes: 10 additions & 0 deletions docs/api/dialog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 <string_view>

+#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<uint32_t> 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();
Expand All @@ -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<dbus::Bus> bus,
base::AtomicFlag* availability_test_complete,
+ unsigned int xdg_portal_required_version,
std::optional<bool> 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) {
Expand All @@ -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);
Expand All @@ -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<base::SequencedTaskRunner> main_task_runner;
Expand All @@ -255,3 +363,23 @@ index d57a52b3ccbd3bd6d390615351ea2ad1e475b157..433f34ac6779611623241cd977dd1214

private:
friend class base::RefCountedThreadSafe<DialogInfo>;
@@ -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_;

15 changes: 15 additions & 0 deletions shell/browser/ui/file_dialog_linux.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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 };
Expand Down Expand Up @@ -200,6 +213,7 @@ class FileChooserDialog : public ui::SelectFileDialog::Listener {

bool ShowOpenDialogSync(const DialogSettings& settings,
std::vector<base::FilePath>* paths) {
LogIfNeededAboutUnsupportedPortalFeature(settings);
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
auto cb = base::BindOnce(
[](base::RepeatingClosure cb, std::vector<base::FilePath>* file_paths,
Expand All @@ -217,6 +231,7 @@ bool ShowOpenDialogSync(const DialogSettings& settings,

void ShowOpenDialog(const DialogSettings& settings,
gin_helper::Promise<gin_helper::Dictionary> promise) {
LogIfNeededAboutUnsupportedPortalFeature(settings);
FileChooserDialog* dialog = new FileChooserDialog();
dialog->RunOpenDialog(std::move(promise), settings);
}
Expand Down

0 comments on commit 4fb5aab

Please sign in to comment.