global_menu_bar_registrar_x11.cc revision 3240926e260ce088908e02ac07a6cf7b0c0cbf44
1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h"
6
7#include "base/bind.h"
8#include "base/logging.h"
9#include "chrome/browser/ui/views/frame/global_menu_bar_x11.h"
10#include "content/public/browser/browser_thread.h"
11
12using content::BrowserThread;
13
14namespace {
15
16const char kAppMenuRegistrarName[] = "com.canonical.AppMenu.Registrar";
17const char kAppMenuRegistrarPath[] = "/com/canonical/AppMenu/Registrar";
18
19}  // namespace
20
21// static
22GlobalMenuBarRegistrarX11* GlobalMenuBarRegistrarX11::GetInstance() {
23  return Singleton<GlobalMenuBarRegistrarX11>::get();
24}
25
26void GlobalMenuBarRegistrarX11::OnWindowMapped(unsigned long xid) {
27  live_xids_.insert(xid);
28
29  if (registrar_proxy_)
30    RegisterXID(xid);
31}
32
33void GlobalMenuBarRegistrarX11::OnWindowUnmapped(unsigned long xid) {
34  if (registrar_proxy_)
35    UnregisterXID(xid);
36
37  live_xids_.erase(xid);
38}
39
40GlobalMenuBarRegistrarX11::GlobalMenuBarRegistrarX11()
41    : registrar_proxy_(NULL) {
42  // libdbusmenu uses the gio version of dbus; I tried using the code in dbus/,
43  // but it looks like that's isn't sharing the bus name with the gio version,
44  // even when |connection_type| is set to SHARED.
45  g_dbus_proxy_new_for_bus(
46      G_BUS_TYPE_SESSION,
47      static_cast<GDBusProxyFlags>(
48          G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
49          G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
50          G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START),
51      NULL,
52      kAppMenuRegistrarName,
53      kAppMenuRegistrarPath,
54      kAppMenuRegistrarName,
55      NULL,  // TODO: Probalby want a real cancelable.
56      static_cast<GAsyncReadyCallback>(OnProxyCreatedThunk),
57      this);
58}
59
60GlobalMenuBarRegistrarX11::~GlobalMenuBarRegistrarX11() {
61  if (registrar_proxy_) {
62    g_signal_handlers_disconnect_by_func(
63        registrar_proxy_,
64        reinterpret_cast<void*>(OnNameOwnerChangedThunk),
65        this);
66    g_object_unref(registrar_proxy_);
67  }
68}
69
70void GlobalMenuBarRegistrarX11::RegisterXID(unsigned long xid) {
71  DCHECK(registrar_proxy_);
72  std::string path = GlobalMenuBarX11::GetPathForWindow(xid);
73
74  // TODO(erg): The mozilla implementation goes to a lot of callback trouble
75  // just to make sure that they react to make sure there's some sort of
76  // cancelable object; including making a whole callback just to handle the
77  // cancelable.
78  //
79  // I don't see any reason why we should care if "RegisterWindow" completes or
80  // not.
81  g_dbus_proxy_call(registrar_proxy_,
82                    "RegisterWindow",
83                    g_variant_new("(uo)", xid, path.c_str()),
84                    G_DBUS_CALL_FLAGS_NONE, -1,
85                    NULL,
86                    NULL,
87                    NULL);
88}
89
90void GlobalMenuBarRegistrarX11::UnregisterXID(unsigned long xid) {
91  DCHECK(registrar_proxy_);
92  std::string path = GlobalMenuBarX11::GetPathForWindow(xid);
93
94  // TODO(erg): The mozilla implementation goes to a lot of callback trouble
95  // just to make sure that they react to make sure there's some sort of
96  // cancelable object; including making a whole callback just to handle the
97  // cancelable.
98  //
99  // I don't see any reason why we should care if "UnregisterWindow" completes
100  // or not.
101  g_dbus_proxy_call(registrar_proxy_,
102                    "UnregisterWindow",
103                    g_variant_new("(u)", xid),
104                    G_DBUS_CALL_FLAGS_NONE, -1,
105                    NULL,
106                    NULL,
107                    NULL);
108}
109
110void GlobalMenuBarRegistrarX11::OnProxyCreated(GObject* source,
111                                               GAsyncResult* result) {
112  GError* error = NULL;
113  GDBusProxy* proxy = g_dbus_proxy_new_for_bus_finish(result, &error);
114  if (error) {
115    g_error_free(error);
116    return;
117  }
118
119  // TODO(erg): Mozilla's implementation has a workaround for GDBus
120  // cancellation here. However, it's marked as fixed. If there's weird
121  // problems with cancelation, look at how they fixed their issues.
122
123  registrar_proxy_ = proxy;
124
125  g_signal_connect(registrar_proxy_, "notify::g-name-owner",
126                   G_CALLBACK(OnNameOwnerChangedThunk), this);
127
128  OnNameOwnerChanged(NULL, NULL);
129}
130
131void GlobalMenuBarRegistrarX11::OnNameOwnerChanged(GObject* /* ignored */,
132                                                   GParamSpec* /* ignored */) {
133  // If the name owner changed, we need to reregister all the live xids with
134  // the system.
135  for (std::set<unsigned long>::const_iterator it = live_xids_.begin();
136       it != live_xids_.end(); ++it) {
137    RegisterXID(*it);
138  }
139}
140