electron/shell/common/platform_util_linux.cc

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

483 lines
16 KiB
C++
Raw Normal View History

// Copyright (c) 2013 GitHub, Inc.
2014-04-25 17:49:37 +08:00
// Use of this source code is governed by the MIT license that can be
2013-12-31 12:59:14 +00:00
// found in the LICENSE file.
#include "shell/common/platform_util.h"
2013-12-31 12:59:14 +00:00
#include <fcntl.h>
2013-12-31 12:59:14 +00:00
#include <stdio.h>
#include <memory>
#include <optional>
#include <queue>
#include <string>
#include <vector>
2013-12-31 12:59:14 +00:00
#include <gdk/gdk.h>
2016-10-20 17:08:05 -07:00
#include "base/cancelable_callback.h"
#include "base/environment.h"
#include "base/files/file_util.h"
chore: remove unused #includes (#42971) * chore: iwyu buildflags.h * chore: iwyu dictionary.h * chore: iwyu arguments.h * chore: iwyu values.h * chore: iwyu compiler_specific.h * chore: iwyu binder_map.h * chore: iwyu <vector> * chore: iwyu <set> * chore: iwyu raw_ptr * chore: iwyu gfx/canvas.h * chore: iwyu gfx/color_utils.h * chore: iwyu base/strings/stringprintf.h * chore: iwyu base/task/thread_pool.h * chore: iwyu base/no_destructor.h * chore: iwyu base/path_service.h * chore: iwyu base/files/file_pathh * chore: iwyu base/strings/sys_string_conversions.h * chore: iwyu base/logging.h * chore: iwyu base/command_line.h * chore: iwyu base/files/file_util.h * chore: iwyu base/files/scoped_file.h * chore: iwyu base/strings/utf_string_conversions.h * chore: iwyu base/environment.h * chore: iwyu base/scoped_observation.h * chore: iwyu base/strings/string_split.h * chore: iwyu base/strings/pattern.h * chore: iwyu base/json/string_escape.h * chore: iwyu base/json/json_reader.h * chore: iwyu base/memory/singleton.h * chore: iwyu base/observer_list.h * chore: iwyu base/timer/timer.h * fixup! chore: iwyu values.h * chore: iwyu shell/browser/browser.h * chore: iwyu base/stl_util.h * chore: iwyu base/strings/string_util.h * chore: iwyu shell/browser/javascript_environment.h * chore: iwyu base/memory/ref_counted.h * chore: iwyu base/environment.h * chore: iwyu content/public/browser/browser_thread.h * chore: remove unused typedef gin_helper::EventEmitter::ValueArray * chore: iwyu gin/wrappable.h * chore: iwyu shell/common/gin_helper/function_template_extensions.h * chore: iwyu shell/common/gin_converters/login_item_settings_converter.h * chore: iwyu shell/common/gin_helper/arguments.h * chore: iwyu ui/gfx/skia_util.h * chore: iwyu ui/gfx/geometry/rect.h * chore: iwyu ui/gfx/image/image.h * chore: iwyu base/strings/strcat.h * chore: iwyu ui/native_theme/native_theme.h * fixup! chore: iwyu shell/browser/javascript_environment.h * fixup! chore: iwyu gfx/canvas.h * fixup! chore: iwyu content/public/browser/browser_thread.h * fixup! chore: iwyu ui/native_theme/native_theme.h * fixup! chore: iwyu ui/native_theme/native_theme.h
2024-07-22 04:31:32 -05:00
#include "base/files/scoped_file.h"
chore: bump chromium to 100.0.4857.0 (main) (#32419) * chore: bump chromium in DEPS to 99.0.4819.0 * chore: update patches * chore: bump chromium in DEPS to 99.0.4824.0 * chore: update patches * chore: bump chromium in DEPS to 99.0.4827.0 * chore: update patches * 3352511: PiP: Add inkdrop and pointer cursor to PiP window buttons https://chromium-review.googlesource.com/c/chromium/src/+/3352511 * 3309164: webhid: Show FIDO devices in the chooser if allowed https://chromium-review.googlesource.com/c/chromium/src/+/3309164 * 3297868: hid: Add experimental HIDDevice.forget() https://chromium-review.googlesource.com/c/chromium/src/+/3297868 * 3362491: [Extensions] Move i18n API to //extensions https://chromium-review.googlesource.com/c/chromium/src/+/3362491 * MCC Refactor step0: Allow embedders to register associated_interface binders with RenderFrameHostImpl::associated_registry_. https://chromium-review.googlesource.com/c/chromium/src/+/3281481 * 3352616: [Gtk] Remove libgtk from the link-line https://chromium-review.googlesource.com/c/chromium/src/+/3352616 * 3249211: Clear-Site-Data support for partitioned cookies https://chromium-review.googlesource.com/c/chromium/src/+/3249211 * [Extensions][COIL] Use [allow|block]list in //extensions/common https://chromium-review.googlesource.com/c/chromium/src/+/3372668 * Begin ScopedUserPrefUpdate migration to modern base::Value https://chromium-review.googlesource.com/c/chromium/src/+/3376154 * [Code Health] Refactor PrefService GetDict + GetList to use base::Value https://chromium-review.googlesource.com/c/chromium/src/+/3343526 * 3354997: [CodeHealth] Remove deprecated SetDictionary method https://chromium-review.googlesource.com/c/chromium/src/+/3354997 * 3287323: Add LacrosPrefStore for lacros settings https://chromium-review.googlesource.com/c/chromium/src/+/3287323 * 3365916: [PA] Clean up remaining lazy commit code https://chromium-review.googlesource.com/c/chromium/src/+/3365916 * [MPArch] Target the external protocol error at the responsible frame. https://chromium-review.googlesource.com/c/chromium/src/+/3011560 * Pass origin to RegisterNonNetworkSubresourceURLLoaderFactories https://chromium-review.googlesource.com/c/chromium/src/+/3350608 * Linux: Send OSCrypt raw encryption key to the Network Service https://chromium-review.googlesource.com/c/chromium/src/+/3320484 * [PlzServiceWorker] Remove remaining references to PlzServiceWorker. https://chromium-review.googlesource.com/c/chromium/src/+/3359441 * chore: fixup for lint * 3327621: Fix tablet mode detection for Win 11. https://chromium-review.googlesource.com/c/chromium/src/+/3327621 * 3342428: ax_mac: move AXTextMarker conversion utils under ui umbrella https://chromium-review.googlesource.com/c/chromium/src/+/3342428 * 3353974: Mac: Use base::Feature for overlay features https://chromium-review.googlesource.com/c/chromium/src/+/3353974 * chore: bump chromium in DEPS to 99.0.4828.0 * chore: update patches * chore: bump chromium in DEPS to 99.0.4837.0 * chore: update patches * chore: update patches * 3379142: Drop FALLTHROUGH macro Ref: https://chromium-review.googlesource.com/c/chromium/src/+/3379142 * 3381749: C++17: Allow use of std::map::try_emplace and std::map::insert_or_assign Ref: https://chromium-review.googlesource.com/c/chromium/src/+/3381749 * chore: bump chromium in DEPS to 99.0.4839.0 * chore: update patches * chore: bump chromium in DEPS to 99.0.4840.0 * chore: bump chromium in DEPS to 99.0.4844.0 * 3395881: [api] Deprecate Local<v8::Context> v8::Object::CreationContext() Ref: https://chromium-review.googlesource.com/c/v8/v8/+/3395881 * chore: update patches * chore: bump chromium in DEPS to 100.0.4845.0 * chore: update patches * chore: bump chromium in DEPS to 100.0.4847.0 * chore: update patches * chore: bump chromium in DEPS to 100.0.4849.0 * chore: update patches * chore: bump chromium in DEPS to 100.0.4851.0 * chore: bump chromium in DEPS to 100.0.4853.0 * update patches * chore: update patches * update patches * 3383599: Fonts Access: Remove prototype that uses a font picker. https://chromium-review.googlesource.com/c/chromium/src/+/3383599 * 3404768: Remove ALLOW_UNUSED macros https://chromium-review.googlesource.com/c/chromium/src/+/3404768 * 3374762: Remove ignore_result.h https://chromium-review.googlesource.com/c/chromium/src/+/3374762 * 3399305: [unseasoned-pdf] Apply proper frame offsets for touch selections https://chromium-review.googlesource.com/c/chromium/src/+/3399305 * 3402210: [Extensions] Don't trigger unload event for already unloaded extension https://chromium-review.googlesource.com/c/chromium/src/+/3402210 * 3410912: Combine URLLoaderClient OnReceiveResponse and OnStartLoadingResponseBody. https://chromium-review.googlesource.com/c/chromium/src/+/3410912 * 3370428: Make the AuthSchemes policy support dynamic refresh https://chromium-review.googlesource.com/c/chromium/src/+/3370428 * 3407603: Finish ScopedUserPrefUpdate migration to modern base::Value https://chromium-review.googlesource.com/c/chromium/src/+/3407603 * 3378352: ozone/x11: move code from //ui/p/x11 to //ui/ozone/p/x11 https://chromium-review.googlesource.com/c/chromium/src/+/3378352 * 3370810: Delete chrome/service, AKA the Cloud Print service process. https://chromium-review.googlesource.com/c/chromium/src/+/3370810 * chore: bump chromium in DEPS to 100.0.4855.0 * chore: update patches * fixup! 3370810: Delete chrome/service, AKA the Cloud Print service process. * revert 3348007 to fix windows build * 3318572: [Code health] Fix gn check errors in //extensions/browser:* https://chromium-review.googlesource.com/c/chromium/src/+/3318572 * fix printing.patch * fix iwyu issue * 3408515: win: Make ShorcutOperation an enum class and modernize names https://chromium-review.googlesource.com/c/chromium/src/+/3408515 * 3388333: [UIA] Remove dead code accessibility_misc_utils.h/cc https://chromium-review.googlesource.com/c/chromium/src/+/3388333 * fix windows build? i hope * patch gn visibility of //ui/ozone/platform/x11 * missing include base/logging.h * use BUILDFLAG for USE_NSS_CERTS https://chromium-review.googlesource.com/c/chromium/src/+/3379123 * defined(OS_*) ==> BUILDFLAG(IS_*) https://bugs.chromium.org/p/chromium/issues/detail?id=1234043 * fixup! 3404768: Remove ALLOW_UNUSED macros * another attempt to fix windows build * temporarily disable the custom scheme service worker test https://github.com/electron/electron/issues/32664 * fix loading mv3 extensions not sure what cl broke this unfort. * fixup! 3404768: Remove ALLOW_UNUSED macros * patch nan https://chromium-review.googlesource.com/c/v8/v8/+/3395880 * fix node test * fix nullptr in FindPdfFrame * patch perfetto to fix build issue on win-ia32 https://source.chromium.org/chromium/_/android/platform/external/perfetto.git/+/bc44c3c7533c00e56f88c06c592d634aecc884be * fix build for linux-x64-testing-no-run-as-node * fix patch * skip <webview>.capturePage() test https://github.com/electron/electron/issues/32705 * test: fix failing tests of focus/blur events of WebContents (#32711) * inherit stdio from app module test child processes this prevents them from timing out due to full stdout buffers * test to see if we can get better logs on windows ci * try again for appveyor log things * skip contentTracing tests on ia32 * ci: disable gpu compositing * drop applied patch * fix merge fail * Revert "ci: disable gpu compositing" This reverts commit 0344129fcb19ea3e87e06c1110d751f22eba3fec. Co-authored-by: electron-roller[bot] <84116207+electron-roller[bot]@users.noreply.github.com> Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org> Co-authored-by: PatchUp <73610968+patchup[bot]@users.noreply.github.com> Co-authored-by: John Kleinschmidt <jkleinsc@github.com> Co-authored-by: VerteDinde <khammond@slack-corp.com> Co-authored-by: VerteDinde <vertedinde@electronjs.org> Co-authored-by: Jeremy Rose <jeremya@chromium.org> Co-authored-by: Jeremy Rose <nornagon@nornagon.net> Co-authored-by: Cheng Zhao <zcbenz@gmail.com> Co-authored-by: deepak1556 <hop2deep@gmail.com>
2022-02-09 18:58:52 -08:00
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/nix/xdg_util.h"
#include "base/no_destructor.h"
#include "base/posix/eintr_wrapper.h"
2013-12-31 12:59:14 +00:00
#include "base/process/kill.h"
#include "base/process/launch.h"
#include "base/run_loop.h"
#include "base/strings/escape.h"
#include "base/strings/string_util.h"
#include "base/task/thread_pool.h"
#include "base/threading/thread_restrictions.h"
#include "base/types/expected.h"
#include "components/dbus/thread_linux/dbus_thread_linux.h"
#include "components/dbus/utils/call_method.h"
#include "components/dbus/utils/check_for_service_and_start.h"
#include "components/dbus/xdg/request.h"
#include "content/public/browser/browser_thread.h"
#include "dbus/bus.h"
#include "dbus/message.h"
#include "dbus/object_proxy.h"
#include "ui/gtk/gtk_compat.h" // nogncheck
#include "electron/electron_gtk_stubs.h"
#include "shell/common/platform_util_internal.h"
2013-12-31 12:59:14 +00:00
#include "url/gurl.h"
#define ELECTRON_TRASH "ELECTRON_TRASH"
namespace platform_util {
void OpenFolder(const base::FilePath& full_path);
}
2013-12-31 12:59:14 +00:00
namespace {
const char kFreedesktopFileManagerName[] = "org.freedesktop.FileManager1";
const char kFreedesktopFileManagerPath[] = "/org/freedesktop/FileManager1";
const char kMethodShowItems[] = "ShowItems";
const char kFreedesktopPortalName[] = "org.freedesktop.portal.Desktop";
const char kFreedesktopPortalPath[] = "/org/freedesktop/portal/desktop";
const char kFreedesktopPortalOpenURI[] = "org.freedesktop.portal.OpenURI";
const char kMethodOpenDirectory[] = "OpenDirectory";
const char kActivationTokenKey[] = "activation_token";
class ShowItemHelper {
public:
static ShowItemHelper& GetInstance() {
static base::NoDestructor<ShowItemHelper> instance;
return *instance;
}
ShowItemHelper() = default;
ShowItemHelper(const ShowItemHelper&) = delete;
ShowItemHelper& operator=(const ShowItemHelper&) = delete;
void ShowItemInFolder(const base::FilePath& full_path) {
if (!bus_) {
bus_ = dbus_thread_linux::GetSharedSessionBus();
}
if (api_type_.has_value()) {
ShowItemInFolderOnApiTypeSet(full_path);
return;
}
bool api_availability_check_in_progress = !pending_requests_.empty();
pending_requests_.push(full_path);
if (!api_availability_check_in_progress) {
// Initiate check to determine if portal or the FileManager API should
// be used. The portal API is always preferred if available.
dbus_utils::CheckForServiceAndStart(
bus_.get(), kFreedesktopPortalName,
base::BindOnce(&ShowItemHelper::CheckPortalRunningResponse,
// Unretained is safe, the ShowItemHelper instance is
// never destroyed.
base::Unretained(this)));
}
}
private:
enum class ApiType { kNone, kPortal, kFileManager };
void ShowItemInFolderOnApiTypeSet(const base::FilePath& full_path) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
CHECK(api_type_.has_value());
switch (*api_type_) {
case ApiType::kPortal:
ShowItemUsingPortal(full_path);
break;
case ApiType::kFileManager:
ShowItemUsingFileManager(full_path);
break;
case ApiType::kNone:
OpenParentFolderFallback(full_path);
break;
}
}
void ProcessPendingRequests() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!bus_) {
return;
}
CHECK(!pending_requests_.empty());
while (!pending_requests_.empty()) {
ShowItemInFolderOnApiTypeSet(pending_requests_.front());
pending_requests_.pop();
}
}
void CheckPortalRunningResponse(std::optional<bool> is_running) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (is_running.value_or(false)) {
api_type_ = ApiType::kPortal;
ProcessPendingRequests();
} else {
// Portal is unavailable.
// Check if FileManager is available.
dbus_utils::CheckForServiceAndStart(
bus_.get(), kFreedesktopFileManagerName,
base::BindOnce(&ShowItemHelper::CheckFileManagerRunningResponse,
// Unretained is safe, the ShowItemHelper instance is
// never destroyed.
base::Unretained(this)));
}
}
void CheckFileManagerRunningResponse(std::optional<bool> is_running) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (is_running.value_or(false)) {
api_type_ = ApiType::kFileManager;
} else {
// Neither portal nor FileManager is available.
api_type_ = ApiType::kNone;
}
ProcessPendingRequests();
}
void ShowItemUsingPortal(const base::FilePath& full_path) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
CHECK(api_type_.has_value());
CHECK_EQ(*api_type_, ApiType::kPortal);
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock()},
base::BindOnce(
[](const base::FilePath& full_path) {
base::ScopedFD fd(HANDLE_EINTR(
open(full_path.value().c_str(), O_RDONLY | O_CLOEXEC)));
return fd;
},
full_path),
base::BindOnce(&ShowItemHelper::ShowItemUsingPortalFdOpened,
// Unretained is safe, the ShowItemHelper instance is
// never destroyed.
base::Unretained(this), full_path));
}
void ShowItemUsingPortalFdOpened(const base::FilePath& full_path,
base::ScopedFD fd) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!bus_) {
return;
}
if (!fd.is_valid()) {
// At least open the parent folder, as long as we're not in the unit
// tests.
OpenParentFolderFallback(full_path);
return;
}
base::nix::CreateXdgActivationToken(base::BindOnce(
&ShowItemHelper::ShowItemUsingPortalWithToken,
// Unretained is safe, the ShowItemHelper instance is never destroyed.
base::Unretained(this), full_path, std::move(fd)));
}
void ShowItemUsingPortalWithToken(const base::FilePath& full_path,
base::ScopedFD fd,
std::string activation_token) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!bus_) {
return;
}
if (!portal_object_proxy_) {
portal_object_proxy_ = bus_->GetObjectProxy(
kFreedesktopPortalName, dbus::ObjectPath(kFreedesktopPortalPath));
}
dbus_xdg::Dictionary options;
options[kActivationTokenKey] =
dbus_utils::Variant::Wrap<"s">(activation_token);
// In the rare occasion that another request comes in before the response is
// received, we will end up overwriting this request object with the new one
// and the response from the first request will not be handled in that case.
// This should be acceptable as it means the two requests were received too
// close to each other from the user and the first one was handled on a best
// effort basis.
portal_open_directory_request_ = std::make_unique<dbus_xdg::Request>(
bus_, portal_object_proxy_, kFreedesktopPortalOpenURI,
kMethodOpenDirectory, std::move(options),
base::BindOnce(&ShowItemHelper::ShowItemUsingPortalResponse,
// Unretained is safe, the ShowItemHelper instance is
// never destroyed.
base::Unretained(this), full_path),
std::string(), std::move(fd));
}
void ShowItemUsingPortalResponse(
const base::FilePath& full_path,
base::expected<dbus_xdg::Dictionary, dbus_xdg::ResponseError> results) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
portal_open_directory_request_.reset();
if (!results.has_value()) {
OpenParentFolderFallback(full_path);
}
}
void ShowItemUsingFileManager(const base::FilePath& full_path) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!bus_) {
return;
}
CHECK(api_type_.has_value());
CHECK_EQ(*api_type_, ApiType::kFileManager);
if (!file_manager_object_proxy_) {
file_manager_object_proxy_ =
bus_->GetObjectProxy(kFreedesktopFileManagerName,
dbus::ObjectPath(kFreedesktopFileManagerPath));
}
std::vector<std::string> file_to_highlight{"file://" + full_path.value()};
dbus_utils::CallMethod<"ass", "">(
file_manager_object_proxy_, kFreedesktopFileManagerName,
kMethodShowItems,
base::BindOnce(&ShowItemHelper::ShowItemUsingFileManagerResponse,
// Unretained is safe, the ShowItemHelper instance is
// never destroyed.
base::Unretained(this), full_path),
std::move(file_to_highlight), /*startup-id=*/"");
}
void ShowItemUsingFileManagerResponse(
const base::FilePath& full_path,
dbus_utils::CallMethodResultSig<""> response) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!response.has_value()) {
// If the bus call fails, at least open the parent folder.
OpenParentFolderFallback(full_path);
}
}
void OpenParentFolderFallback(const base::FilePath& full_path) {
platform_util::OpenFolder(full_path.DirName());
}
scoped_refptr<dbus::Bus> bus_;
std::optional<ApiType> api_type_;
// The proxy objects are owned by `bus_`.
raw_ptr<dbus::ObjectProxy> portal_object_proxy_ = nullptr;
raw_ptr<dbus::ObjectProxy> file_manager_object_proxy_ = nullptr;
std::unique_ptr<dbus_xdg::Request> portal_open_directory_request_;
// Requests that are queued until the API availability is determined.
std::queue<base::FilePath> pending_requests_;
};
// Descriptions pulled from https://linux.die.net/man/1/xdg-open
std::string GetErrorDescription(int error_code) {
switch (error_code) {
case 1:
return "Error in command line syntax";
case 2:
return "The item does not exist";
case 3:
return "A required tool could not be found";
case 4:
return "The action failed";
default:
return "";
}
}
bool XDGUtil(const std::vector<std::string>& argv,
const base::FilePath& working_directory,
const bool wait_for_exit,
const bool focus_launched_process,
platform_util::OpenCallback callback) {
2013-12-31 12:59:14 +00:00
base::LaunchOptions options;
if (focus_launched_process) {
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
base::RepeatingClosure quit_loop = run_loop.QuitClosure();
base::nix::CreateLaunchOptionsWithXdgActivation(base::BindOnce(
[](base::RepeatingClosure quit_loop, base::LaunchOptions* options_out,
base::LaunchOptions options) {
*options_out = std::move(options);
std::move(quit_loop).Run();
},
std::move(quit_loop), &options));
run_loop.Run();
}
options.current_directory = working_directory;
options.allow_new_privs = true;
2013-12-31 12:59:14 +00:00
// xdg-open can fall back on mailcap which eventually might plumb through
// to a command that needs a terminal. Set the environment variable telling
// it that we definitely don't have a terminal available and that it should
// bring up a new terminal if necessary. See "man mailcap".
options.environment["MM_NOTTTY"] = "1";
2013-12-31 12:59:14 +00:00
base::Process process = base::LaunchProcess(argv, options);
if (!process.IsValid())
return false;
if (wait_for_exit) {
base::ScopedAllowBaseSyncPrimitivesForTesting
allow_sync; // required by WaitForExit
int exit_code = -1;
bool success = process.WaitForExit(&exit_code);
if (!callback.is_null())
std::move(callback).Run(GetErrorDescription(exit_code));
return success ? (exit_code == 0) : false;
}
base::EnsureProcessGetsReaped(std::move(process));
return true;
2013-12-31 12:59:14 +00:00
}
bool XDGOpen(const base::FilePath& working_directory,
const std::string& path,
const bool wait_for_exit,
platform_util::OpenCallback callback) {
return XDGUtil({"xdg-open", path}, working_directory, wait_for_exit,
/*focus_launched_process=*/true, std::move(callback));
2013-12-31 12:59:14 +00:00
}
2016-04-08 08:32:45 +01:00
bool XDGEmail(const std::string& email, const bool wait_for_exit) {
return XDGUtil({"xdg-email", email}, base::FilePath(), wait_for_exit,
/*focus_launched_process=*/true,
platform_util::OpenCallback());
2013-12-31 12:59:14 +00:00
}
} // namespace
namespace platform_util {
void ShowItemInFolder(const base::FilePath& full_path) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
ShowItemHelper::GetInstance().ShowItemInFolder(full_path);
2013-12-31 12:59:14 +00:00
}
void OpenPath(const base::FilePath& full_path, OpenCallback callback) {
// This is async, so we don't care about the return value.
XDGOpen(full_path.DirName(), full_path.value(), false, std::move(callback));
}
void OpenFolder(const base::FilePath& full_path) {
if (!base::DirectoryExists(full_path))
return;
XDGOpen(full_path.DirName(), ".", false, platform_util::OpenCallback());
2013-12-31 12:59:14 +00:00
}
void OpenExternal(const GURL& url,
const OpenExternalOptions& options,
OpenCallback callback) {
// Don't wait for exit, since we don't want to wait for the browser/email
// client window to close before returning
if (url.SchemeIs("mailto")) {
bool success = XDGEmail(url.spec(), false);
std::move(callback).Run(success ? "" : "Failed to open path");
} else {
bool success = XDGOpen(base::FilePath(), url.spec(), false,
platform_util::OpenCallback());
std::move(callback).Run(success ? "" : "Failed to open path");
}
}
bool MoveItemToTrash(const base::FilePath& full_path, bool delete_on_fail) {
auto env = base::Environment::Create();
// find the trash method
std::string trash = env->GetVar(ELECTRON_TRASH).value_or("");
if (trash.empty()) {
// Determine desktop environment and set accordingly.
const auto desktop_env(base::nix::GetDesktopEnvironment(env.get()));
if (desktop_env == base::nix::DESKTOP_ENVIRONMENT_KDE4 ||
desktop_env == base::nix::DESKTOP_ENVIRONMENT_KDE5) {
trash = "kioclient5";
} else if (desktop_env == base::nix::DESKTOP_ENVIRONMENT_KDE3) {
trash = "kioclient";
}
}
// build the invocation
std::vector<std::string> argv;
const auto& filename = full_path.value();
if (trash == "kioclient5" || trash == "kioclient") {
argv = {trash, "move", filename, "trash:/"};
} else if (trash == "trash-cli") {
argv = {"trash-put", filename};
} else if (trash == "gvfs-trash") {
argv = {"gvfs-trash", filename}; // deprecated, but still exists
} else {
argv = {"gio", "trash", filename};
}
return XDGUtil(argv, base::FilePath(), true, /*focus_launched_process=*/false,
platform_util::OpenCallback());
2013-12-31 12:59:14 +00:00
}
namespace internal {
bool PlatformTrashItem(const base::FilePath& full_path, std::string* error) {
if (!MoveItemToTrash(full_path, false)) {
// TODO(nornagon): at least include the exit code?
*error = "Failed to move item to trash";
return false;
}
return true;
}
} // namespace internal
2013-12-31 12:59:14 +00:00
void Beep() {
// `gdk_display_beep` is actually stubbed out, and the function pointer the
// stub uses may be nullptr. We need to initialize the stub here to ensure
// that is not the case so that we can avoid a crash.
// TODO: move this elsewhere if / when we start using stubs for more
// GDK functions than just `gdk_display_beep`.
if (!electron::IsElectron_gdkInitialized()) {
electron::InitializeElectron_gdk(gtk::GetLibGdk());
CHECK(electron::IsElectron_gdkInitialized())
<< "Failed to initialize libgdk";
}
auto* display = gdk_display_get_default();
if (!display)
return;
gdk_display_beep(display);
2013-12-31 12:59:14 +00:00
}
std::optional<std::string> GetDesktopName() {
return base::Environment::Create()->GetVar("CHROME_DESKTOP");
}
std::optional<std::string> GetXdgAppId() {
auto name = GetDesktopName();
if (!name)
return {};
// remove '.desktop' file suffix, if present
if (std::string_view suffix = ".desktop"; name->ends_with(suffix))
name->resize(std::size(*name) - std::size(suffix));
return *name;
}
2013-12-31 12:59:14 +00:00
} // namespace platform_util