0

Move GlobalShortcutListenerLinux to //ui/base

The rest of the shortcut listeners were already moved but this was
split out because of additional dependencies that needed to be removed.

The update to metadata_unittest is due to a conflicting symbol
declaration when the global accelerator listener unittest is added.

Change-Id: I81763cb556eadc72794ea0aca9ed80eb4346c722
Bug: 378487333
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6097375
Reviewed-by: David Bertoni <dbertoni@chromium.org>
Reviewed-by: Steven Valdez <svaldez@chromium.org>
Code-Coverage: findit-for-me@appspot.gserviceaccount.com <findit-for-me@appspot.gserviceaccount.com>
Reviewed-by: David Yeung <dayeung@chromium.org>
Reviewed-by: Anthony Cui <cuianthony@chromium.org>
Reviewed-by: Thomas Anderson <thomasanderson@chromium.org>
Commit-Queue: Alison Gale <agale@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1401876}
This commit is contained in:
Alison Gale
2025-01-03 10:16:03 -08:00
committed by Chromium LUCI CQ
parent 072c567861
commit a9ad4ac349
16 changed files with 202 additions and 145 deletions

@ -1366,13 +1366,6 @@ source_set("extensions") {
if (is_linux) {
sources +=
[ "api/image_writer_private/removable_storage_provider_linux.cc" ]
if (use_dbus) {
sources += [
"global_shortcut_listener_linux.cc",
"global_shortcut_listener_linux.h",
]
deps += [ "//components/dbus" ]
}
}
if (is_linux || is_chromeos) {

@ -18,23 +18,10 @@
#include "ui/base/accelerators/command.h"
#include "ui/base/accelerators/global_accelerator_listener/global_accelerator_listener.h"
#if BUILDFLAG(IS_LINUX) && BUILDFLAG(USE_DBUS)
#include "base/feature_list.h"
#include "chrome/browser/extensions/global_shortcut_listener_linux.h"
#endif
using content::BrowserThread;
namespace extensions {
namespace {
#if BUILDFLAG(IS_LINUX) && BUILDFLAG(USE_DBUS)
BASE_FEATURE(kGlobalShortcutsPortal,
"GlobalShortcutsPortal",
base::FEATURE_DISABLED_BY_DEFAULT);
#endif
} // namespace
// static
GlobalShortcutListener* GlobalShortcutListener::GetInstance() {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
@ -46,13 +33,6 @@ GlobalShortcutListener* GlobalShortcutListener::GetInstance() {
return instance;
}
#if BUILDFLAG(IS_OZONE) && BUILDFLAG(IS_LINUX) && BUILDFLAG(USE_DBUS)
if (base::FeatureList::IsEnabled(kGlobalShortcutsPortal)) {
static GlobalShortcutListenerLinux* const linux_instance =
new GlobalShortcutListenerLinux(nullptr);
return linux_instance;
}
#endif
return nullptr;
}

@ -26,17 +26,12 @@ namespace extensions {
// applies only to extensions.
class GlobalShortcutListener {
public:
class Observer : public ui::GlobalAcceleratorListener::Observer {
public:
// Called when a command should be executed directly.
virtual void ExecuteCommand(const std::string& accelerator_group_id,
const std::string& command_id) = 0;
};
using Observer = ui::GlobalAcceleratorListener::Observer;
GlobalShortcutListener(const GlobalShortcutListener&) = delete;
GlobalShortcutListener& operator=(const GlobalShortcutListener&) = delete;
virtual ~GlobalShortcutListener();
~GlobalShortcutListener();
// The instance may be nullptr.
static GlobalShortcutListener* GetInstance();
@ -59,7 +54,7 @@ class GlobalShortcutListener {
// Stop listening for all accelerators of the given |observer|, does nothing
// if shortcut handling is suspended.
virtual void UnregisterAccelerators(Observer* observer);
void UnregisterAccelerators(Observer* observer);
// Suspend/Resume global shortcut handling. Note that when suspending,
// RegisterAccelerator/UnregisterAccelerator/UnregisterAccelerators are not
@ -71,13 +66,13 @@ class GlobalShortcutListener {
// Returns true if shortcut registration is managed by the desktop. False
// indicates registration is managed by us.
virtual bool IsRegistrationHandledExternally() const;
bool IsRegistrationHandledExternally() const;
// Called when an extension's commands are registered.
virtual void OnCommandsChanged(const std::string& accelerator_group_id,
const std::string& profile_id,
const ui::CommandMap& commands,
Observer* observer);
void OnCommandsChanged(const std::string& accelerator_group_id,
const std::string& profile_id,
const ui::CommandMap& commands,
Observer* observer);
protected:
explicit GlobalShortcutListener(

@ -97,7 +97,7 @@ class ExtensionsGlobalShortcutListenerTest : public testing::Test {
std::unique_ptr<BaseGlobalShortcutListenerForTesting> ui_listener_;
std::unique_ptr<TestObserver> observer_ = nullptr;
// A UI environment is required since GlobalShortcutListener (base class of
// GlobalShortcutListenerLinux) CHECKs that it's running on a UI thread.
// GlobalAcceleratorListenerLinux) CHECKs that it's running on a UI thread.
content::BrowserTaskEnvironment task_environment_;
};

@ -55,6 +55,12 @@ void GlicBackgroundModeManager::OnKeyPressed(
controller_->Show();
}
void GlicBackgroundModeManager::ExecuteCommand(
const std::string& accelerator_group_id,
const std::string& command_id) {
// TODO(crbug.com/385194502): Handle Linux.
}
void GlicBackgroundModeManager::EnterBackgroundMode() {
if (!keep_alive_) {
keep_alive_ = std::make_unique<ScopedKeepAlive>(

@ -39,6 +39,8 @@ class GlicBackgroundModeManager
// ui::GlobalAcceleratorListener::Observer
void OnKeyPressed(const ui::Accelerator& accelerator) override;
void ExecuteCommand(const std::string& accelerator_group_id,
const std::string& command_id) override;
ui::Accelerator RegisteredHotkeyForTesting() {
return actual_registered_hotkey_;

@ -9097,7 +9097,6 @@ test("unit_tests") {
if (use_dbus) {
sources += [
"../browser/dbus_memory_pressure_evaluator_linux_unittest.cc",
"../browser/extensions/global_shortcut_listener_linux_unittest.cc",
"../browser/notifications/notification_platform_bridge_linux_unittest.cc",
"../browser/ui/views/dark_mode_manager_linux_unittest.cc",
]

@ -1298,6 +1298,16 @@ test("ui_base_unittests") {
if (is_linux) {
sources += [ "linux/xdg_shortcut_unittest.cc" ]
}
if (is_linux && use_dbus) {
sources += [ "accelerators/global_accelerator_listener/global_accelerator_listener_linux_unittest.cc" ]
deps += [
"//components/dbus",
"//content/test:test_support",
"//dbus:test_support",
"//ui/base/accelerators/global_accelerator_listener",
]
}
}
if (is_mac) {

@ -45,4 +45,14 @@ source_set("global_accelerator_listener") {
]
deps += [ "//build/config/linux/dbus:buildflags" ]
}
if (is_linux) {
if (use_dbus) {
sources += [
"global_accelerator_listener_linux.cc",
"global_accelerator_listener_linux.h",
]
deps += [ "//components/dbus" ]
}
}
}

@ -1,3 +1,6 @@
include_rules = [
"+components/dbus",
"+content/public",
"+crypto",
"+dbus",
]

@ -8,6 +8,7 @@
#include <map>
#include "base/memory/raw_ptr.h"
#include "ui/base/accelerators/command.h"
namespace ui {
@ -22,6 +23,10 @@ class GlobalAcceleratorListener {
public:
// Called when your global accelerator is struck.
virtual void OnKeyPressed(const ui::Accelerator& accelerator) = 0;
// Called when a command should be executed
// directly.
virtual void ExecuteCommand(const std::string& accelerator_group_id,
const std::string& command_id) = 0;
};
GlobalAcceleratorListener(const GlobalAcceleratorListener&) = delete;
@ -60,6 +65,12 @@ class GlobalAcceleratorListener {
virtual void StopListeningForAccelerator(
const ui::Accelerator& accelerator) = 0;
// Called when a group of commands are registered.
virtual void OnCommandsChanged(const std::string& accelerator_group_id,
const std::string& profile_id,
const CommandMap& commands,
Observer* observer) {}
protected:
GlobalAcceleratorListener();

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/global_shortcut_listener_linux.h"
#include "ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_linux.h"
#include <algorithm>
#include <string>
@ -14,7 +14,6 @@
#include "base/nix/xdg_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/extensions/global_shortcut_listener.h"
#include "components/dbus/properties/types.h"
#include "components/dbus/thread_linux/dbus_thread_linux.h"
#include "components/dbus/utils/check_for_service_and_start.h"
@ -27,14 +26,14 @@
#include "ui/base/accelerators/command.h"
#include "ui/base/linux/xdg_shortcut.h"
namespace extensions {
namespace ui {
using DbusShortcut = DbusStruct<DbusString, DbusDictionary>;
using DbusShortcuts = DbusArray<DbusShortcut>;
GlobalShortcutListenerLinux::GlobalShortcutListenerLinux(
GlobalAcceleratorListenerLinux::GlobalAcceleratorListenerLinux(
scoped_refptr<dbus::Bus> bus)
: GlobalShortcutListener(nullptr), bus_(std::move(bus)) {
: bus_(std::move(bus)) {
if (!bus_) {
dbus::Bus::Options options;
options.bus_type = dbus::Bus::SESSION;
@ -47,18 +46,18 @@ GlobalShortcutListenerLinux::GlobalShortcutListenerLinux(
global_shortcuts_proxy_->ConnectToSignal(
kGlobalShortcutsInterface, kSignalActivated,
base::BindRepeating(&GlobalShortcutListenerLinux::OnActivatedSignal,
base::BindRepeating(&GlobalAcceleratorListenerLinux::OnActivatedSignal,
weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&GlobalShortcutListenerLinux::OnSignalConnected,
base::BindOnce(&GlobalAcceleratorListenerLinux::OnSignalConnected,
weak_ptr_factory_.GetWeakPtr()));
dbus_xdg::SetSystemdScopeUnitNameForXdgPortal(
bus_.get(),
base::BindOnce(&GlobalShortcutListenerLinux::OnSystemdUnitStarted,
base::BindOnce(&GlobalAcceleratorListenerLinux::OnSystemdUnitStarted,
weak_ptr_factory_.GetWeakPtr()));
}
GlobalShortcutListenerLinux::~GlobalShortcutListenerLinux() {
GlobalAcceleratorListenerLinux::~GlobalAcceleratorListenerLinux() {
// Normally GlobalShortcutListener outlives the browser process, so this
// destructor won't normally get called. It's okay for the sessions not to be
// closed explicitly, but this destructor is left here for testing purposes,
@ -75,16 +74,16 @@ GlobalShortcutListenerLinux::~GlobalShortcutListenerLinux() {
FROM_HERE, base::BindOnce(&dbus::Bus::ShutdownAndBlock, bus_));
}
void GlobalShortcutListenerLinux::OnSystemdUnitStarted(
void GlobalAcceleratorListenerLinux::OnSystemdUnitStarted(
dbus_xdg::SystemdUnitStatus) {
// Intentionally ignoring the status.
dbus_utils::CheckForServiceAndStart(
bus_.get(), kPortalServiceName,
base::BindOnce(&GlobalShortcutListenerLinux::OnServiceStarted,
base::BindOnce(&GlobalAcceleratorListenerLinux::OnServiceStarted,
weak_ptr_factory_.GetWeakPtr()));
}
void GlobalShortcutListenerLinux::OnServiceStarted(
void GlobalAcceleratorListenerLinux::OnServiceStarted(
std::optional<bool> service_started) {
service_started_ = service_started.value_or(false);
@ -98,7 +97,7 @@ void GlobalShortcutListenerLinux::OnServiceStarted(
}
}
void GlobalShortcutListenerLinux::CreateSession(SessionMapPair& pair) {
void GlobalAcceleratorListenerLinux::CreateSession(SessionMapPair& pair) {
CHECK(!bus_->GetConnectionName().empty());
const SessionKey& session_key = pair.first;
@ -116,11 +115,27 @@ void GlobalShortcutListenerLinux::CreateSession(SessionMapPair& pair) {
bus_, global_shortcuts_proxy_, kGlobalShortcutsInterface,
kMethodCreateSession, DbusParameters(),
MakeDbusDictionary("session_handle_token", DbusString(session_token)),
base::BindOnce(&GlobalShortcutListenerLinux::OnCreateSession,
base::BindOnce(&GlobalAcceleratorListenerLinux::OnCreateSession,
weak_ptr_factory_.GetWeakPtr(), session_key));
}
void GlobalShortcutListenerLinux::UnregisterAccelerators(Observer* observer) {
void GlobalAcceleratorListenerLinux::StartListening() {}
void GlobalAcceleratorListenerLinux::StopListening() {}
bool GlobalAcceleratorListenerLinux::StartListeningForAccelerator(
const ui::Accelerator& accelerator) {
// Shortcut registration is now handled in OnCommandsChanged()
return false;
}
void GlobalAcceleratorListenerLinux::StopListeningForAccelerator(
const ui::Accelerator& accelerator) {
// Shortcut unregistration is now handled per extension
}
void GlobalAcceleratorListenerLinux::UnregisterAccelerators(
Observer* observer) {
std::vector<SessionKey> remove;
for (const auto& [key, context] : session_map_) {
if (context->observer == observer) {
@ -132,11 +147,11 @@ void GlobalShortcutListenerLinux::UnregisterAccelerators(Observer* observer) {
}
}
bool GlobalShortcutListenerLinux::IsRegistrationHandledExternally() const {
bool GlobalAcceleratorListenerLinux::IsRegistrationHandledExternally() const {
return true;
}
void GlobalShortcutListenerLinux::OnCommandsChanged(
void GlobalAcceleratorListenerLinux::OnCommandsChanged(
const std::string& accelerator_group_id,
const std::string& profile_id,
const ui::CommandMap& commands,
@ -158,8 +173,9 @@ void GlobalShortcutListenerLinux::OnCommandsChanged(
dbus::MethodCall method_call(kSessionInterface, kMethodCloseSession);
session_context.session_proxy->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&GlobalShortcutListenerLinux::RecreateSessionOnClosed,
weak_ptr_factory_.GetWeakPtr(), session_key));
base::BindOnce(
&GlobalAcceleratorListenerLinux::RecreateSessionOnClosed,
weak_ptr_factory_.GetWeakPtr(), session_key));
}
return;
}
@ -172,7 +188,7 @@ void GlobalShortcutListenerLinux::OnCommandsChanged(
}
}
void GlobalShortcutListenerLinux::OnCreateSession(
void GlobalAcceleratorListenerLinux::OnCreateSession(
const SessionKey& session_key,
base::expected<DbusDictionary, dbus_xdg::ResponseError> results) {
if (!results.has_value()) {
@ -207,11 +223,11 @@ void GlobalShortcutListenerLinux::OnCreateSession(
kMethodListShortcuts,
DbusObjectPath(session_context->session_proxy->object_path()),
DbusDictionary(),
base::BindOnce(&GlobalShortcutListenerLinux::OnListShortcuts,
base::BindOnce(&GlobalAcceleratorListenerLinux::OnListShortcuts,
weak_ptr_factory_.GetWeakPtr(), session_key));
}
void GlobalShortcutListenerLinux::OnListShortcuts(
void GlobalAcceleratorListenerLinux::OnListShortcuts(
const SessionKey& session_key,
base::expected<DbusDictionary, dbus_xdg::ResponseError> results) {
if (!results.has_value()) {
@ -253,7 +269,7 @@ void GlobalShortcutListenerLinux::OnListShortcuts(
}
}
void GlobalShortcutListenerLinux::BindShortcuts(
void GlobalAcceleratorListenerLinux::BindShortcuts(
SessionContext& session_context) {
dbus::MethodCall method_call(kGlobalShortcutsInterface, kMethodBindShortcuts);
dbus::MessageWriter writer(&method_call);
@ -283,12 +299,12 @@ void GlobalShortcutListenerLinux::BindShortcuts(
DbusObjectPath(session_context.session_proxy->object_path()),
std::move(shortcuts), std::move(empty_parent_window)),
DbusDictionary(),
base::BindOnce(&GlobalShortcutListenerLinux::OnBindShortcuts,
base::BindOnce(&GlobalAcceleratorListenerLinux::OnBindShortcuts,
weak_ptr_factory_.GetWeakPtr()));
session_context.bind_shortcuts_called = true;
}
void GlobalShortcutListenerLinux::OnBindShortcuts(
void GlobalAcceleratorListenerLinux::OnBindShortcuts(
base::expected<DbusDictionary, dbus_xdg::ResponseError> results) {
if (!results.has_value()) {
LOG(ERROR) << "Failed to call BindShortcuts (error code "
@ -300,7 +316,7 @@ void GlobalShortcutListenerLinux::OnBindShortcuts(
// the bound shortcuts, but it's currently not needed.
}
void GlobalShortcutListenerLinux::RecreateSessionOnClosed(
void GlobalAcceleratorListenerLinux::RecreateSessionOnClosed(
const SessionKey& session_key,
dbus::Response* response) {
auto session_it = session_map_.find(session_key);
@ -310,7 +326,7 @@ void GlobalShortcutListenerLinux::RecreateSessionOnClosed(
CreateSession(*session_it);
}
void GlobalShortcutListenerLinux::OnActivatedSignal(dbus::Signal* signal) {
void GlobalAcceleratorListenerLinux::OnActivatedSignal(dbus::Signal* signal) {
dbus::MessageReader reader(signal);
dbus::ObjectPath session_handle;
std::string shortcut_id;
@ -332,7 +348,7 @@ void GlobalShortcutListenerLinux::OnActivatedSignal(dbus::Signal* signal) {
}
}
void GlobalShortcutListenerLinux::OnSignalConnected(
void GlobalAcceleratorListenerLinux::OnSignalConnected(
const std::string& interface_name,
const std::string& signal_name,
bool success) {
@ -342,23 +358,23 @@ void GlobalShortcutListenerLinux::OnSignalConnected(
}
}
std::string GlobalShortcutListenerLinux::SessionKey::GetTokenKey() const {
std::string GlobalAcceleratorListenerLinux::SessionKey::GetTokenKey() const {
return kSessionTokenPrefix +
base::HexEncode(
crypto::SHA256HashString(accelerator_group_id + profile_id))
.substr(0, 32);
}
GlobalShortcutListenerLinux::SessionContext::SessionContext(
GlobalAcceleratorListenerLinux::SessionContext::SessionContext(
Observer* observer,
const ui::CommandMap& commands)
: observer(observer), commands(commands) {}
GlobalShortcutListenerLinux::SessionContext::~SessionContext() {
GlobalAcceleratorListenerLinux::SessionContext::~SessionContext() {
if (session_proxy) {
bus->RemoveObjectProxy(kPortalServiceName, session_proxy->object_path(),
base::DoNothing());
}
}
} // namespace extensions
} // namespace ui

@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_LINUX_H_
#define CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_LINUX_H_
#ifndef UI_BASE_ACCELERATORS_GLOBAL_ACCELERATOR_LISTENER_GLOBAL_ACCELERATOR_LISTENER_LINUX_H_
#define UI_BASE_ACCELERATORS_GLOBAL_ACCELERATOR_LISTENER_GLOBAL_ACCELERATOR_LISTENER_LINUX_H_
#include <memory>
#include <optional>
@ -13,34 +13,36 @@
#include "base/containers/flat_map.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/extensions/global_shortcut_listener.h"
#include "components/dbus/xdg/request.h"
#include "dbus/bus.h"
#include "dbus/object_proxy.h"
#include "ui/base/accelerators/command.h"
#include "ui/base/accelerators/global_accelerator_listener/global_accelerator_listener.h"
namespace dbus_xdg {
class Request;
enum class SystemdUnitStatus;
} // namespace dbus_xdg
namespace extensions {
namespace ui {
// Linux-specific implementation of the GlobalShortcutListener class that
// listens for global shortcuts using the org.freedesktop.portal.GlobalShortcuts
// interface.
class GlobalShortcutListenerLinux : public GlobalShortcutListener {
class GlobalAcceleratorListenerLinux : public GlobalAcceleratorListener {
public:
explicit GlobalShortcutListenerLinux(scoped_refptr<dbus::Bus> bus);
explicit GlobalAcceleratorListenerLinux(scoped_refptr<dbus::Bus> bus);
GlobalShortcutListenerLinux(const GlobalShortcutListenerLinux&) = delete;
GlobalShortcutListenerLinux& operator=(const GlobalShortcutListenerLinux&) =
GlobalAcceleratorListenerLinux(const GlobalAcceleratorListenerLinux&) =
delete;
GlobalAcceleratorListenerLinux& operator=(
const GlobalAcceleratorListenerLinux&) = delete;
~GlobalShortcutListenerLinux() override;
~GlobalAcceleratorListenerLinux() override;
private:
FRIEND_TEST_ALL_PREFIXES(GlobalShortcutListenerLinuxTest, OnCommandsChanged);
FRIEND_TEST_ALL_PREFIXES(GlobalAcceleratorListenerLinuxTest,
OnCommandsChanged);
// These are exposed in the header for testing.
static constexpr char kPortalServiceName[] = "org.freedesktop.portal.Desktop";
@ -85,9 +87,14 @@ class GlobalShortcutListenerLinux : public GlobalShortcutListener {
base::flat_map<SessionKey, std::unique_ptr<SessionContext>>;
using SessionMapPair = std::pair<SessionKey, std::unique_ptr<SessionContext>>;
// GlobalShortcutListener:
void UnregisterAccelerators(Observer* observer) override;
bool IsRegistrationHandledExternally() const override;
// GlobalAcceleratorListener:
void StartListening() override;
void StopListening() override;
bool StartListeningForAccelerator(
const ui::Accelerator& accelerator) override;
void StopListeningForAccelerator(const ui::Accelerator& accelerator) override;
void UnregisterAccelerators(Observer* observer);
bool IsRegistrationHandledExternally() const;
void OnCommandsChanged(const std::string& accelerator_group_id,
const std::string& profile_id,
const ui::CommandMap& commands,
@ -131,9 +138,9 @@ class GlobalShortcutListenerLinux : public GlobalShortcutListener {
// One session per extension.
base::flat_map<SessionKey, std::unique_ptr<SessionContext>> session_map_;
base::WeakPtrFactory<GlobalShortcutListenerLinux> weak_ptr_factory_{this};
base::WeakPtrFactory<GlobalAcceleratorListenerLinux> weak_ptr_factory_{this};
};
} // namespace extensions
} // namespace ui
#endif // CHROME_BROWSER_EXTENSIONS_GLOBAL_SHORTCUT_LISTENER_LINUX_H_
#endif // UI_BASE_ACCELERATORS_GLOBAL_ACCELERATOR_LISTENER_GLOBAL_ACCELERATOR_LISTENER_LINUX_H_

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/global_shortcut_listener_linux.h"
#include "ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_linux.h"
#include "base/memory/scoped_refptr.h"
#include "base/nix/xdg_util.h"
@ -13,7 +13,6 @@
#include "dbus/message.h"
#include "dbus/mock_bus.h"
#include "dbus/mock_object_proxy.h"
#include "extensions/common/command.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/accelerators/accelerator.h"
@ -25,7 +24,7 @@ using ::testing::AtLeast;
using ::testing::Invoke;
using ::testing::Return;
namespace extensions {
namespace ui {
namespace {
@ -44,12 +43,12 @@ MATCHER_P2(MatchMethod, interface, member, "") {
using DbusShortcuts = DbusArray<DbusStruct<DbusString, DbusDictionary>>;
class MockObserver final : public GlobalShortcutListener::Observer {
class MockObserver final : public GlobalAcceleratorListener::Observer {
public:
virtual ~MockObserver() = default;
void OnKeyPressed(const ui::Accelerator& accelerator) override {
// GlobalShortcutListenerLinux uses ExecuteCommand() instead.
// GlobalAcceleratorListenerLinux uses ExecuteCommand() instead.
NOTREACHED();
}
@ -58,9 +57,9 @@ class MockObserver final : public GlobalShortcutListener::Observer {
const std::string& command_name));
};
TEST(GlobalShortcutListenerLinuxTest, OnCommandsChanged) {
TEST(GlobalAcceleratorListenerLinuxTest, OnCommandsChanged) {
// A UI environment is required since GlobalShortcutListener (base class of
// GlobalShortcutListenerLinux) CHECKs that it's running on a UI thread.
// GlobalAcceleratorListenerLinux) CHECKs that it's running on a UI thread.
content::BrowserTaskEnvironment task_environment;
auto mock_bus = base::MakeRefCounted<dbus::MockBus>(dbus::Bus::Options());
@ -70,8 +69,8 @@ TEST(GlobalShortcutListenerLinuxTest, OnCommandsChanged) {
auto mock_global_shortcuts_proxy =
base::MakeRefCounted<dbus::MockObjectProxy>(
mock_bus.get(), GlobalShortcutListenerLinux::kPortalServiceName,
dbus::ObjectPath(GlobalShortcutListenerLinux::kPortalObjectPath));
mock_bus.get(), GlobalAcceleratorListenerLinux::kPortalServiceName,
dbus::ObjectPath(GlobalAcceleratorListenerLinux::kPortalObjectPath));
auto mock_systemd_proxy = base::MakeRefCounted<dbus::MockObjectProxy>(
mock_bus.get(), "org.freedesktop.systemd1",
@ -97,8 +96,8 @@ TEST(GlobalShortcutListenerLinuxTest, OnCommandsChanged) {
EXPECT_CALL(
*mock_bus,
GetObjectProxy(
GlobalShortcutListenerLinux::kPortalServiceName,
dbus::ObjectPath(GlobalShortcutListenerLinux::kPortalObjectPath)))
GlobalAcceleratorListenerLinux::kPortalServiceName,
dbus::ObjectPath(GlobalAcceleratorListenerLinux::kPortalObjectPath)))
.WillRepeatedly(Return(mock_global_shortcuts_proxy.get()));
EXPECT_CALL(*mock_bus, GetConnectionName()).WillRepeatedly(Return(kBusName));
@ -125,7 +124,7 @@ TEST(GlobalShortcutListenerLinuxTest, OnCommandsChanged) {
std::string service_name;
EXPECT_TRUE(reader.PopString(&service_name));
EXPECT_EQ(service_name,
GlobalShortcutListenerLinux::kPortalServiceName);
GlobalAcceleratorListenerLinux::kPortalServiceName);
auto response = dbus::Response::CreateEmpty();
dbus::MessageWriter writer(response.get());
@ -135,10 +134,10 @@ TEST(GlobalShortcutListenerLinuxTest, OnCommandsChanged) {
// Activated signal
dbus::ObjectProxy::SignalCallback activated_callback;
EXPECT_CALL(
*mock_global_shortcuts_proxy,
DoConnectToSignal(GlobalShortcutListenerLinux::kGlobalShortcutsInterface,
GlobalShortcutListenerLinux::kSignalActivated, _, _))
EXPECT_CALL(*mock_global_shortcuts_proxy,
DoConnectToSignal(
GlobalAcceleratorListenerLinux::kGlobalShortcutsInterface,
GlobalAcceleratorListenerLinux::kSignalActivated, _, _))
.WillOnce(Invoke(
[&](const std::string& interface_name, const std::string& signal_name,
dbus::ObjectProxy::SignalCallback signal_callback,
@ -152,7 +151,7 @@ TEST(GlobalShortcutListenerLinuxTest, OnCommandsChanged) {
}));
auto global_shortcut_listener =
std::make_unique<GlobalShortcutListenerLinux>(mock_bus);
std::make_unique<GlobalAcceleratorListenerLinux>(mock_bus);
auto observer = std::make_unique<MockObserver>();
// These object proxies have unique generated names, so are initialized when
@ -167,7 +166,7 @@ TEST(GlobalShortcutListenerLinuxTest, OnCommandsChanged) {
const dbus::ObjectPath& object_path) -> dbus::ObjectProxy* {
// The first call in the sequence is for the session proxy.
session_proxy = base::MakeRefCounted<dbus::MockObjectProxy>(
mock_bus.get(), GlobalShortcutListenerLinux::kPortalServiceName,
mock_bus.get(), GlobalAcceleratorListenerLinux::kPortalServiceName,
object_path);
return session_proxy.get();
};
@ -177,7 +176,7 @@ TEST(GlobalShortcutListenerLinuxTest, OnCommandsChanged) {
const dbus::ObjectPath& object_path) -> dbus::ObjectProxy* {
// CreateSession
create_session_request_proxy = base::MakeRefCounted<dbus::MockObjectProxy>(
mock_bus.get(), GlobalShortcutListenerLinux::kPortalServiceName,
mock_bus.get(), GlobalAcceleratorListenerLinux::kPortalServiceName,
object_path);
EXPECT_CALL(*create_session_request_proxy, DoConnectToSignal(_, _, _, _))
.WillOnce(Invoke(
@ -208,7 +207,7 @@ TEST(GlobalShortcutListenerLinuxTest, OnCommandsChanged) {
const dbus::ObjectPath& object_path) -> dbus::ObjectProxy* {
// ListShortcuts
list_shortcuts_request_proxy = base::MakeRefCounted<dbus::MockObjectProxy>(
mock_bus.get(), GlobalShortcutListenerLinux::kPortalServiceName,
mock_bus.get(), GlobalAcceleratorListenerLinux::kPortalServiceName,
object_path);
EXPECT_CALL(*list_shortcuts_request_proxy, DoConnectToSignal(_, _, _, _))
.WillOnce(Invoke(
@ -237,7 +236,7 @@ TEST(GlobalShortcutListenerLinuxTest, OnCommandsChanged) {
const dbus::ObjectPath& object_path) -> dbus::ObjectProxy* {
// BindShortcuts
bind_shortcuts_request_proxy = base::MakeRefCounted<dbus::MockObjectProxy>(
mock_bus.get(), GlobalShortcutListenerLinux::kPortalServiceName,
mock_bus.get(), GlobalAcceleratorListenerLinux::kPortalServiceName,
object_path);
EXPECT_CALL(*bind_shortcuts_request_proxy, DoConnectToSignal(_, _, _, _))
.WillOnce(Invoke(
@ -262,7 +261,7 @@ TEST(GlobalShortcutListenerLinuxTest, OnCommandsChanged) {
EXPECT_CALL(
*mock_bus,
GetObjectProxy(GlobalShortcutListenerLinux::kPortalServiceName, _))
GetObjectProxy(GlobalAcceleratorListenerLinux::kPortalServiceName, _))
.WillOnce(Invoke(get_object_proxy_session))
.WillOnce(Invoke(get_object_proxy_create_session))
.WillOnce(Invoke(get_object_proxy_list_shortcuts))
@ -272,8 +271,8 @@ TEST(GlobalShortcutListenerLinuxTest, OnCommandsChanged) {
EXPECT_CALL(
*mock_global_shortcuts_proxy,
DoCallMethod(
MatchMethod(GlobalShortcutListenerLinux::kGlobalShortcutsInterface,
GlobalShortcutListenerLinux::kMethodCreateSession),
MatchMethod(GlobalAcceleratorListenerLinux::kGlobalShortcutsInterface,
GlobalAcceleratorListenerLinux::kMethodCreateSession),
_, _))
.WillOnce(Invoke([&](dbus::MethodCall* method_call, int timeout_ms,
dbus::ObjectProxy::ResponseCallback* callback) {
@ -297,8 +296,8 @@ TEST(GlobalShortcutListenerLinuxTest, OnCommandsChanged) {
EXPECT_CALL(
*mock_global_shortcuts_proxy,
DoCallMethod(
MatchMethod(GlobalShortcutListenerLinux::kGlobalShortcutsInterface,
GlobalShortcutListenerLinux::kMethodListShortcuts),
MatchMethod(GlobalAcceleratorListenerLinux::kGlobalShortcutsInterface,
GlobalAcceleratorListenerLinux::kMethodListShortcuts),
_, _))
.WillOnce(Invoke([&](dbus::MethodCall* method_call, int timeout_ms,
dbus::ObjectProxy::ResponseCallback* callback) {
@ -318,8 +317,8 @@ TEST(GlobalShortcutListenerLinuxTest, OnCommandsChanged) {
EXPECT_CALL(
*mock_global_shortcuts_proxy,
DoCallMethod(
MatchMethod(GlobalShortcutListenerLinux::kGlobalShortcutsInterface,
GlobalShortcutListenerLinux::kMethodBindShortcuts),
MatchMethod(GlobalAcceleratorListenerLinux::kGlobalShortcutsInterface,
GlobalAcceleratorListenerLinux::kMethodBindShortcuts),
_, _))
.WillOnce(Invoke([&](dbus::MethodCall* method_call, int timeout_ms,
dbus::ObjectProxy::ResponseCallback* callback) {
@ -338,18 +337,18 @@ TEST(GlobalShortcutListenerLinuxTest, OnCommandsChanged) {
}));
ui::CommandMap commands;
commands[kCommandName] = Command(kCommandName, kShortcutDescription,
Command::AcceleratorToString(ui::Accelerator(
ui::VKEY_A, ui::EF_CONTROL_DOWN)),
/*global=*/true);
commands[kCommandName] = ui::Command(kCommandName, kShortcutDescription,
/*global=*/true);
commands[kCommandName].set_accelerator(
ui::Accelerator(ui::VKEY_A, ui::EF_CONTROL_DOWN));
global_shortcut_listener->OnCommandsChanged(kExtensionId, kProfileId,
commands, observer.get());
// Simulate the Activated signal
EXPECT_CALL(*observer, ExecuteCommand(kExtensionId, kCommandName));
dbus::Signal signal(GlobalShortcutListenerLinux::kGlobalShortcutsInterface,
GlobalShortcutListenerLinux::kSignalActivated);
dbus::Signal signal(GlobalAcceleratorListenerLinux::kGlobalShortcutsInterface,
GlobalAcceleratorListenerLinux::kSignalActivated);
dbus::MessageWriter writer(&signal);
writer.AppendObjectPath(session_proxy->object_path());
writer.AppendString(kCommandName);
@ -357,13 +356,14 @@ TEST(GlobalShortcutListenerLinuxTest, OnCommandsChanged) {
activated_callback.Run(&signal);
// Cleanup
EXPECT_CALL(*session_proxy,
DoCallMethod(
MatchMethod(GlobalShortcutListenerLinux::kSessionInterface,
GlobalShortcutListenerLinux::kMethodCloseSession),
_, _));
EXPECT_CALL(
*session_proxy,
DoCallMethod(
MatchMethod(GlobalAcceleratorListenerLinux::kSessionInterface,
GlobalAcceleratorListenerLinux::kMethodCloseSession),
_, _));
EXPECT_CALL(*mock_bus, ShutdownAndBlock());
global_shortcut_listener.reset();
}
} // namespace extensions
} // namespace ui

@ -12,8 +12,21 @@
#include "ui/base/accelerators/accelerator.h"
#include "ui/ozone/public/ozone_platform.h"
#if BUILDFLAG(IS_LINUX) && BUILDFLAG(USE_DBUS)
#include "base/feature_list.h"
#include "ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_linux.h"
#endif
using content::BrowserThread;
namespace {
#if BUILDFLAG(IS_LINUX) && BUILDFLAG(USE_DBUS)
BASE_FEATURE(kGlobalShortcutsPortal,
"GlobalShortcutsPortal",
base::FEATURE_DISABLED_BY_DEFAULT);
#endif
} // namespace
namespace ui {
// static
@ -21,7 +34,19 @@ GlobalAcceleratorListener* GlobalAcceleratorListener::GetInstance() {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
static const base::NoDestructor<std::unique_ptr<GlobalAcceleratorListener>>
instance(GlobalAcceleratorListenerOzone::Create());
return instance->get();
if (instance->get()) {
return instance->get();
}
#if BUILDFLAG(IS_LINUX) && BUILDFLAG(USE_DBUS)
if (base::FeatureList::IsEnabled(kGlobalShortcutsPortal)) {
static GlobalAcceleratorListenerLinux* const linux_instance =
new GlobalAcceleratorListenerLinux(nullptr);
return linux_instance;
}
#endif
return nullptr;
}
// static

@ -116,18 +116,18 @@ class ClassPropertyMetaDataTestClass : public MetadataTestBaseClass {
struct MetadataTestClassNoMetadata : public MetadataTestBaseClass {};
DEFINE_UI_CLASS_PROPERTY_KEY(int, kIntKey, -1)
DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(gfx::Insets, kOwnedInsetsKey1)
DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(gfx::Insets, kOwnedInsetsKey2)
DEFINE_UI_CLASS_PROPERTY_KEY(gfx::Insets*, kInsetsKey1, nullptr)
DEFINE_UI_CLASS_PROPERTY_KEY(gfx::Insets*, kInsetsKey2, nullptr)
DEFINE_UI_CLASS_PROPERTY_TYPE(gfx::Insets*)
DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(gfx::Point, kOwnedInsetsKey1)
DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(gfx::Point, kOwnedInsetsKey2)
DEFINE_UI_CLASS_PROPERTY_KEY(gfx::Point*, kInsetsKey1, nullptr)
DEFINE_UI_CLASS_PROPERTY_KEY(gfx::Point*, kInsetsKey2, nullptr)
DEFINE_UI_CLASS_PROPERTY_TYPE(gfx::Point*)
BEGIN_METADATA(ClassPropertyMetaDataTestClass)
ADD_CLASS_PROPERTY_METADATA(int, kIntKey)
ADD_CLASS_PROPERTY_METADATA(gfx::Insets, kOwnedInsetsKey1)
ADD_CLASS_PROPERTY_METADATA(gfx::Insets*, kOwnedInsetsKey2)
ADD_CLASS_PROPERTY_METADATA(gfx::Insets, kInsetsKey1)
ADD_CLASS_PROPERTY_METADATA(gfx::Insets*, kInsetsKey2)
ADD_CLASS_PROPERTY_METADATA(gfx::Point, kOwnedInsetsKey1)
ADD_CLASS_PROPERTY_METADATA(gfx::Point*, kOwnedInsetsKey2)
ADD_CLASS_PROPERTY_METADATA(gfx::Point, kInsetsKey1)
ADD_CLASS_PROPERTY_METADATA(gfx::Point*, kInsetsKey2)
END_METADATA
TEST_F(MetadataTest, TestFloatMetadataPropertyAccess) {
@ -225,7 +225,7 @@ TEST_F(MetadataTest, TestMetaDataFile) {
TEST_F(MetadataTest, TestClassPropertyMetaData) {
ClassPropertyMetaDataTestClass test_class;
gfx::Insets insets1(8), insets2 = insets1;
gfx::Point insets1(8, 10), insets2 = insets1;
std::map<std::string, std::u16string> expected_kv = {
{"kIntKey", u"-1"},
@ -256,10 +256,10 @@ TEST_F(MetadataTest, TestClassPropertyMetaData) {
test_class.SetProperty(kInsetsKey2, &insets2);
expected_kv = {{"kIntKey", u"1"},
{"kOwnedInsetsKey1", u"8,8,8,8"},
{"kOwnedInsetsKey2", u"&{8,8,8,8}"},
{"kInsetsKey1", u"8,8,8,8"},
{"kInsetsKey2", u"&{8,8,8,8}"}};
{"kOwnedInsetsKey1", u"8,10"},
{"kOwnedInsetsKey2", u"&{8,10}"},
{"kInsetsKey1", u"8,10"},
{"kInsetsKey2", u"&{8,10}"}};
verify();
}