| // 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; |
| } |
| } |
| } |