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