blob: 35d08bb20751d4076e3d397282e2276375963e85 [file] [log] [blame]
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/frame/dbus_appmenu_registrar.h"
#include "base/check_op.h"
#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/no_destructor.h"
#include "base/notreached.h"
#include "chrome/browser/ui/views/frame/dbus_appmenu.h"
#include "components/dbus/thread_linux/dbus_thread_linux.h"
#include "components/dbus/utils/call_method.h"
#include "dbus/bus.h"
#include "dbus/object_path.h"
#include "dbus/object_proxy.h"
#include "ui/platform_window/extensions/wayland_extension.h"
#include "ui/platform_window/extensions/x11_extension.h"
namespace {
const char kAppMenuRegistrarName[] = "com.canonical.AppMenu.Registrar";
const char kAppMenuRegistrarPath[] = "/com/canonical/AppMenu/Registrar";
const char kAppMenuRegistrarInterface[] = "com.canonical.AppMenu.Registrar";
} // namespace
// static
DbusAppmenuRegistrar* DbusAppmenuRegistrar::GetInstance() {
static base::NoDestructor<DbusAppmenuRegistrar> instance;
return instance.get();
}
void DbusAppmenuRegistrar::OnMenuBarCreated(DbusAppmenu* menu) {
// Make sure insertion succeeds, we should not already be tracking `menu`.
CHECK(menus_.insert({menu, kUninitialized}).second);
if (service_has_owner_) {
InitializeMenu(menu);
}
}
void DbusAppmenuRegistrar::OnMenuBarDestroyed(DbusAppmenu* menu) {
DCHECK(base::Contains(menus_, menu));
if (menus_[menu] == kRegistered) {
if (auto* toplevel_extension =
ui::GetWaylandToplevelExtension(*menu->platform_window())) {
toplevel_extension->UnsetAppmenu();
} else if (ui::GetX11Extension(*menu->platform_window())) {
dbus_utils::CallMethod<"u", "">(
registrar_proxy_, kAppMenuRegistrarInterface, "UnregisterWindow",
base::DoNothing(), menu->browser_frame_id());
}
}
menus_.erase(menu);
}
DbusAppmenuRegistrar::DbusAppmenuRegistrar()
: bus_(dbus_thread_linux::GetSharedSessionBus()) {
registrar_proxy_ = bus_->GetObjectProxy(
kAppMenuRegistrarName, dbus::ObjectPath(kAppMenuRegistrarPath));
dbus::Bus::ServiceOwnerChangeCallback callback =
base::BindRepeating(&DbusAppmenuRegistrar::OnNameOwnerChanged,
weak_ptr_factory_.GetWeakPtr());
bus_->ListenForServiceOwnerChange(kAppMenuRegistrarName, callback);
bus_->GetServiceOwner(kAppMenuRegistrarName, callback);
}
void DbusAppmenuRegistrar::InitializeMenu(DbusAppmenu* menu) {
DCHECK(base::Contains(menus_, menu));
DCHECK_EQ(menus_[menu], kUninitialized);
menus_[menu] = kInitializing;
menu->Initialize(base::BindOnce(&DbusAppmenuRegistrar::OnMenuInitialized,
weak_ptr_factory_.GetWeakPtr(), menu));
}
void DbusAppmenuRegistrar::RegisterMenu(DbusAppmenu* menu) {
DCHECK(base::Contains(menus_, menu));
DCHECK(menus_[menu] == kInitializeSucceeded || menus_[menu] == kRegistered);
menus_[menu] = kRegistered;
if (auto* toplevel_extension =
ui::GetWaylandToplevelExtension(*menu->platform_window())) {
toplevel_extension->SetAppmenu(bus_->GetConnectionName(), menu->GetPath());
} else if (ui::GetX11Extension(*menu->platform_window())) {
dbus_utils::CallMethod<"uo", "">(
registrar_proxy_, kAppMenuRegistrarInterface, "RegisterWindow",
base::DoNothing(), menu->browser_frame_id(),
dbus::ObjectPath(menu->GetPath()));
}
}
void DbusAppmenuRegistrar::OnMenuInitialized(DbusAppmenu* menu, bool success) {
DCHECK(base::Contains(menus_, menu));
DCHECK(menus_[menu] == kInitializing);
menus_[menu] = success ? kInitializeSucceeded : kInitializeFailed;
if (success && service_has_owner_) {
RegisterMenu(menu);
}
}
void DbusAppmenuRegistrar::OnNameOwnerChanged(
const std::string& service_owner) {
service_has_owner_ = !service_owner.empty();
// If the name owner changed, we need to reregister all the live menus with
// the system.
for (const auto& pair : menus_) {
DbusAppmenu* menu = pair.first;
switch (pair.second) {
case kUninitialized:
if (service_has_owner_) {
InitializeMenu(menu);
}
break;
case kInitializing:
// Wait for Initialize() to finish.
break;
case kInitializeFailed:
// Don't try to recover.
break;
case kInitializeSucceeded:
if (service_has_owner_) {
RegisterMenu(menu);
}
break;
case kRegistered:
if (service_has_owner_) {
RegisterMenu(menu);
} else {
menus_[menu] = kInitializeSucceeded;
}
break;
}
}
}