- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / frame / global_menu_bar_x11.cc
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_x11.h"
6
7 #include <dlfcn.h>
8 #include <glib-object.h>
9
10 #include "base/debug/leak_annotations.h"
11 #include "base/logging.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "chrome/app/chrome_command_ids.h"
18 #include "chrome/browser/chrome_notification_types.h"
19 #include "chrome/browser/history/top_sites.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/sessions/tab_restore_service.h"
22 #include "chrome/browser/sessions/tab_restore_service_factory.h"
23 #include "chrome/browser/ui/browser.h"
24 #include "chrome/browser/ui/browser_commands.h"
25 #include "chrome/browser/ui/browser_tab_restore_service_delegate.h"
26 #include "chrome/browser/ui/views/frame/browser_desktop_root_window_host_x11.h"
27 #include "chrome/browser/ui/views/frame/browser_view.h"
28 #include "chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h"
29 #include "chrome/common/pref_names.h"
30 #include "content/public/browser/notification_source.h"
31 #include "grit/generated_resources.h"
32 #include "ui/base/accelerators/menu_label_accelerator_util_linux.h"
33 #include "ui/base/l10n/l10n_util.h"
34 #include "ui/events/keycodes/keyboard_code_conversion_x.h"
35 #include "ui/gfx/text_elider.h"
36
37 // libdbusmenu-glib types
38 typedef struct _DbusmenuMenuitem DbusmenuMenuitem;
39 typedef DbusmenuMenuitem* (*dbusmenu_menuitem_new_func)();
40 typedef bool (*dbusmenu_menuitem_child_add_position_func)(
41     DbusmenuMenuitem* parent,
42     DbusmenuMenuitem* child,
43     unsigned int position);
44 typedef DbusmenuMenuitem* (*dbusmenu_menuitem_child_append_func)(
45     DbusmenuMenuitem* parent,
46     DbusmenuMenuitem* child);
47 typedef bool (*dbusmenu_menuitem_child_delete_func)(
48     DbusmenuMenuitem* parent,
49     DbusmenuMenuitem* child);
50 typedef GList* (*dbusmenu_menuitem_get_children_func)(
51     DbusmenuMenuitem* item);
52 typedef DbusmenuMenuitem* (*dbusmenu_menuitem_property_set_func)(
53     DbusmenuMenuitem* item,
54     const char* property,
55     const char* value);
56 typedef DbusmenuMenuitem* (*dbusmenu_menuitem_property_set_variant_func)(
57     DbusmenuMenuitem* item,
58     const char* property,
59     GVariant* value);
60 typedef DbusmenuMenuitem* (*dbusmenu_menuitem_property_set_bool_func)(
61     DbusmenuMenuitem* item,
62     const char* property,
63     bool value);
64 typedef DbusmenuMenuitem* (*dbusmenu_menuitem_property_set_int_func)(
65     DbusmenuMenuitem* item,
66     const char* property,
67     int value);
68
69 typedef struct _DbusmenuServer      DbusmenuServer;
70 typedef DbusmenuServer* (*dbusmenu_server_new_func)(const char* object);
71 typedef void (*dbusmenu_server_set_root_func)(DbusmenuServer* self,
72                                               DbusmenuMenuitem* root);
73
74 // A line in the static menu definitions.
75 struct GlobalMenuBarCommand {
76   int str_id;
77   int command;
78   int tag;
79 };
80
81 namespace {
82
83 // Retrieved functions from libdbusmenu-glib.
84
85 // DbusmenuMenuItem methods:
86 dbusmenu_menuitem_new_func menuitem_new = NULL;
87 dbusmenu_menuitem_get_children_func menuitem_get_children = NULL;
88 dbusmenu_menuitem_child_add_position_func menuitem_child_add_position = NULL;
89 dbusmenu_menuitem_child_append_func menuitem_child_append = NULL;
90 dbusmenu_menuitem_child_delete_func menuitem_child_delete = NULL;
91 dbusmenu_menuitem_property_set_func menuitem_property_set = NULL;
92 dbusmenu_menuitem_property_set_variant_func menuitem_property_set_variant =
93     NULL;
94 dbusmenu_menuitem_property_set_bool_func menuitem_property_set_bool = NULL;
95 dbusmenu_menuitem_property_set_int_func menuitem_property_set_int = NULL;
96
97 // DbusmenuServer methods:
98 dbusmenu_server_new_func server_new = NULL;
99 dbusmenu_server_set_root_func server_set_root = NULL;
100
101 // Properties that we set on menu items:
102 const char kPropertyEnabled[] = "enabled";
103 const char kPropertyLabel[] = "label";
104 const char kPropertyShortcut[] = "shortcut";
105 const char kPropertyType[] = "type";
106 const char kPropertyToggleType[] = "toggle-type";
107 const char kPropertyToggleState[] = "toggle-state";
108 const char kPropertyVisible[] = "visible";
109
110 const char kTypeCheckmark[] = "checkmark";
111 const char kTypeSeparator[] = "separator";
112
113 // Data set on GObjectgs.
114 const char kTypeTag[] = "type-tag";
115 const char kHistoryItem[] = "history-item";
116
117 // The maximum number of most visited items to display.
118 const unsigned int kMostVisitedCount = 8;
119
120 // The number of recently closed items to get.
121 const unsigned int kRecentlyClosedCount = 8;
122
123 // Menus more than this many chars long will get trimmed.
124 const int kMaximumMenuWidthInChars = 50;
125
126 // Constants used in menu definitions.
127 const int MENU_SEPARATOR =-1;
128 const int MENU_END = -2;
129 const int MENU_DISABLED_ID = -3;
130
131 // These tag values are used to refer to menu items.
132 const int TAG_MOST_VISITED = 1;
133 const int TAG_RECENTLY_CLOSED = 2;
134 const int TAG_MOST_VISITED_HEADER = 3;
135 const int TAG_RECENTLY_CLOSED_HEADER = 4;
136
137 GlobalMenuBarCommand file_menu[] = {
138   { IDS_NEW_TAB, IDC_NEW_TAB },
139   { IDS_NEW_WINDOW, IDC_NEW_WINDOW },
140   { IDS_NEW_INCOGNITO_WINDOW, IDC_NEW_INCOGNITO_WINDOW },
141   { IDS_REOPEN_CLOSED_TABS_LINUX, IDC_RESTORE_TAB },
142   { IDS_OPEN_FILE_LINUX, IDC_OPEN_FILE },
143   { IDS_OPEN_LOCATION_LINUX, IDC_FOCUS_LOCATION },
144
145   { MENU_SEPARATOR, MENU_SEPARATOR },
146
147   { IDS_CREATE_SHORTCUTS, IDC_CREATE_SHORTCUTS },
148
149   { MENU_SEPARATOR, MENU_SEPARATOR },
150
151   { IDS_CLOSE_WINDOW_LINUX, IDC_CLOSE_WINDOW },
152   { IDS_CLOSE_TAB_LINUX, IDC_CLOSE_TAB },
153   { IDS_SAVE_PAGE, IDC_SAVE_PAGE },
154
155   { MENU_SEPARATOR, MENU_SEPARATOR },
156
157   { IDS_PRINT, IDC_PRINT },
158
159   { MENU_END, MENU_END }
160 };
161
162 GlobalMenuBarCommand edit_menu[] = {
163   { IDS_CUT, IDC_CUT },
164   { IDS_COPY, IDC_COPY },
165   { IDS_PASTE, IDC_PASTE },
166
167   { MENU_SEPARATOR, MENU_SEPARATOR },
168
169   { IDS_FIND, IDC_FIND },
170
171   { MENU_SEPARATOR, MENU_SEPARATOR },
172
173   { IDS_PREFERENCES, IDC_OPTIONS },
174
175   { MENU_END, MENU_END }
176 };
177
178 GlobalMenuBarCommand view_menu[] = {
179   { IDS_SHOW_BOOKMARK_BAR, IDC_SHOW_BOOKMARK_BAR },
180
181   { MENU_SEPARATOR, MENU_SEPARATOR },
182
183   { IDS_STOP_MENU_LINUX, IDC_STOP },
184   { IDS_RELOAD_MENU_LINUX, IDC_RELOAD },
185
186   { MENU_SEPARATOR, MENU_SEPARATOR },
187
188   { IDS_FULLSCREEN, IDC_FULLSCREEN },
189   { IDS_TEXT_DEFAULT_LINUX, IDC_ZOOM_NORMAL },
190   { IDS_TEXT_BIGGER_LINUX, IDC_ZOOM_PLUS },
191   { IDS_TEXT_SMALLER_LINUX, IDC_ZOOM_MINUS },
192
193   { MENU_END, MENU_END }
194 };
195
196 GlobalMenuBarCommand history_menu[] = {
197   { IDS_HISTORY_HOME_LINUX, IDC_HOME },
198   { IDS_HISTORY_BACK_LINUX, IDC_BACK },
199   { IDS_HISTORY_FORWARD_LINUX, IDC_FORWARD },
200
201   { MENU_SEPARATOR, MENU_SEPARATOR },
202
203   { IDS_HISTORY_VISITED_LINUX, MENU_DISABLED_ID, TAG_MOST_VISITED_HEADER },
204
205   { MENU_SEPARATOR, MENU_SEPARATOR },
206
207   { IDS_HISTORY_CLOSED_LINUX, MENU_DISABLED_ID, TAG_RECENTLY_CLOSED_HEADER },
208
209   { MENU_SEPARATOR, MENU_SEPARATOR },
210
211   { IDS_SHOWFULLHISTORY_LINK, IDC_SHOW_HISTORY },
212
213   { MENU_END, MENU_END }
214 };
215
216 GlobalMenuBarCommand tools_menu[] = {
217   { IDS_SHOW_DOWNLOADS, IDC_SHOW_DOWNLOADS },
218   { IDS_SHOW_HISTORY, IDC_SHOW_HISTORY },
219   { IDS_SHOW_EXTENSIONS, IDC_MANAGE_EXTENSIONS },
220
221   { MENU_SEPARATOR, MENU_SEPARATOR },
222
223   { IDS_TASK_MANAGER, IDC_TASK_MANAGER },
224   { IDS_CLEAR_BROWSING_DATA, IDC_CLEAR_BROWSING_DATA },
225
226   { MENU_SEPARATOR, MENU_SEPARATOR },
227
228   { IDS_VIEW_SOURCE, IDC_VIEW_SOURCE },
229   { IDS_DEV_TOOLS, IDC_DEV_TOOLS },
230   { IDS_DEV_TOOLS_CONSOLE, IDC_DEV_TOOLS_CONSOLE },
231   { IDS_DEV_TOOLS_DEVICES, IDC_DEV_TOOLS_DEVICES },
232
233   { MENU_END, MENU_END }
234 };
235
236 GlobalMenuBarCommand help_menu[] = {
237 #if defined(GOOGLE_CHROME_BUILD)
238   { IDS_FEEDBACK, IDC_FEEDBACK },
239 #endif
240   { IDS_HELP_PAGE , IDC_HELP_PAGE_VIA_MENU },
241   { MENU_END, MENU_END }
242 };
243
244 void EnsureMethodsLoaded() {
245   static bool attempted_load = false;
246   if (attempted_load)
247     return;
248   attempted_load = true;
249
250   void* dbusmenu_lib = dlopen("libdbusmenu-glib.so", RTLD_LAZY);
251   if (!dbusmenu_lib)
252     dbusmenu_lib = dlopen("libdbusmenu-glib.so.4", RTLD_LAZY);
253   if (!dbusmenu_lib)
254     return;
255
256   // DbusmenuMenuItem methods.
257   menuitem_new = reinterpret_cast<dbusmenu_menuitem_new_func>(
258       dlsym(dbusmenu_lib, "dbusmenu_menuitem_new"));
259   menuitem_child_add_position =
260       reinterpret_cast<dbusmenu_menuitem_child_add_position_func>(
261           dlsym(dbusmenu_lib, "dbusmenu_menuitem_child_add_position"));
262   menuitem_child_append = reinterpret_cast<dbusmenu_menuitem_child_append_func>(
263       dlsym(dbusmenu_lib, "dbusmenu_menuitem_child_append"));
264   menuitem_child_delete = reinterpret_cast<dbusmenu_menuitem_child_delete_func>(
265       dlsym(dbusmenu_lib, "dbusmenu_menuitem_child_delete"));
266   menuitem_get_children = reinterpret_cast<dbusmenu_menuitem_get_children_func>(
267       dlsym(dbusmenu_lib, "dbusmenu_menuitem_get_children"));
268   menuitem_property_set = reinterpret_cast<dbusmenu_menuitem_property_set_func>(
269       dlsym(dbusmenu_lib, "dbusmenu_menuitem_property_set"));
270   menuitem_property_set_variant =
271       reinterpret_cast<dbusmenu_menuitem_property_set_variant_func>(
272           dlsym(dbusmenu_lib, "dbusmenu_menuitem_property_set_variant"));
273   menuitem_property_set_bool =
274       reinterpret_cast<dbusmenu_menuitem_property_set_bool_func>(
275           dlsym(dbusmenu_lib, "dbusmenu_menuitem_property_set_bool"));
276   menuitem_property_set_int =
277       reinterpret_cast<dbusmenu_menuitem_property_set_int_func>(
278           dlsym(dbusmenu_lib, "dbusmenu_menuitem_property_set_int"));
279
280   // DbusmenuServer methods.
281   server_new = reinterpret_cast<dbusmenu_server_new_func>(
282       dlsym(dbusmenu_lib, "dbusmenu_server_new"));
283   server_set_root = reinterpret_cast<dbusmenu_server_set_root_func>(
284       dlsym(dbusmenu_lib, "dbusmenu_server_set_root"));
285 }
286
287 }  // namespace
288
289 struct GlobalMenuBarX11::HistoryItem {
290   HistoryItem() : session_id(0) {}
291
292   // The title for the menu item.
293   string16 title;
294   // The URL that will be navigated to if the user selects this item.
295   GURL url;
296
297   // This ID is unique for a browser session and can be passed to the
298   // TabRestoreService to re-open the closed window or tab that this
299   // references. A non-0 session ID indicates that this is an entry can be
300   // restored that way. Otherwise, the URL will be used to open the item and
301   // this ID will be 0.
302   SessionID::id_type session_id;
303
304   // If the HistoryItem is a window, this will be the vector of tabs. Note
305   // that this is a list of weak references. The |menu_item_map_| is the owner
306   // of all items. If it is not a window, then the entry is a single page and
307   // the vector will be empty.
308   std::vector<HistoryItem*> tabs;
309
310  private:
311   DISALLOW_COPY_AND_ASSIGN(HistoryItem);
312 };
313
314 GlobalMenuBarX11::GlobalMenuBarX11(BrowserView* browser_view,
315                                    BrowserDesktopRootWindowHostX11* host)
316     : browser_(browser_view->browser()),
317       profile_(browser_->profile()),
318       browser_view_(browser_view),
319       host_(host),
320       server_(NULL),
321       root_item_(NULL),
322       history_menu_(NULL),
323       top_sites_(NULL),
324       tab_restore_service_(NULL),
325       weak_ptr_factory_(this) {
326   EnsureMethodsLoaded();
327
328   if (server_new)
329     host_->AddObserver(this);
330 }
331
332 GlobalMenuBarX11::~GlobalMenuBarX11() {
333   if (server_) {
334     Disable();
335
336     if (tab_restore_service_)
337       tab_restore_service_->RemoveObserver(this);
338
339     g_object_unref(server_);
340     host_->RemoveObserver(this);
341   }
342 }
343
344 // static
345 std::string GlobalMenuBarX11::GetPathForWindow(unsigned long xid) {
346   return base::StringPrintf("/com/canonical/menu/%lX", xid);
347 }
348
349 DbusmenuMenuitem* GlobalMenuBarX11::BuildSeparator() {
350   DbusmenuMenuitem* item = menuitem_new();
351   menuitem_property_set(item, kPropertyType, kTypeSeparator);
352   menuitem_property_set_bool(item, kPropertyVisible, true);
353   return item;
354 }
355
356 DbusmenuMenuitem* GlobalMenuBarX11::BuildMenuItem(
357     const std::string& label,
358     int tag_id) {
359   DbusmenuMenuitem* item = menuitem_new();
360   menuitem_property_set(item, kPropertyLabel, label.c_str());
361   menuitem_property_set_bool(item, kPropertyVisible, true);
362
363   if (tag_id)
364     g_object_set_data(G_OBJECT(item), kTypeTag, GINT_TO_POINTER(tag_id));
365
366   return item;
367 }
368
369 void GlobalMenuBarX11::InitServer(unsigned long xid) {
370   std::string path = GetPathForWindow(xid);
371   {
372     ANNOTATE_SCOPED_MEMORY_LEAK; // http://crbug.com/314087
373     server_ = server_new(path.c_str());
374   }
375
376   root_item_ = menuitem_new();
377   menuitem_property_set(root_item_, kPropertyLabel, "Root");
378   menuitem_property_set_bool(root_item_, kPropertyVisible, true);
379
380   // First build static menu content.
381   BuildStaticMenu(root_item_, IDS_FILE_MENU_LINUX, file_menu);
382   BuildStaticMenu(root_item_, IDS_EDIT_MENU_LINUX, edit_menu);
383   BuildStaticMenu(root_item_, IDS_VIEW_MENU_LINUX, view_menu);
384   history_menu_ = BuildStaticMenu(
385       root_item_, IDS_HISTORY_MENU_LINUX, history_menu);
386   BuildStaticMenu(root_item_, IDS_TOOLS_MENU_LINUX, tools_menu);
387   BuildStaticMenu(root_item_, IDS_HELP_MENU_LINUX, help_menu);
388
389   // We have to connect to |history_menu_item|'s "activate" signal instead of
390   // |history_menu|'s "show" signal because we are not supposed to modify the
391   // menu during "show"
392   g_signal_connect(history_menu_, "about-to-show",
393                    G_CALLBACK(OnHistoryMenuAboutToShowThunk), this);
394
395   for (CommandIDMenuItemMap::const_iterator it = id_to_menu_item_.begin();
396        it != id_to_menu_item_.end(); ++it) {
397     menuitem_property_set_bool(it->second, kPropertyEnabled,
398                                chrome::IsCommandEnabled(browser_, it->first));
399
400     ui::Accelerator accelerator;
401     if (browser_view_->GetAccelerator(it->first, &accelerator))
402       RegisterAccelerator(it->second, accelerator);
403
404     chrome::AddCommandObserver(browser_, it->first, this);
405   }
406
407   pref_change_registrar_.Init(browser_->profile()->GetPrefs());
408   pref_change_registrar_.Add(
409       prefs::kShowBookmarkBar,
410       base::Bind(&GlobalMenuBarX11::OnBookmarkBarVisibilityChanged,
411                  base::Unretained(this)));
412   OnBookmarkBarVisibilityChanged();
413
414   top_sites_ = profile_->GetTopSites();
415   if (top_sites_) {
416     GetTopSitesData();
417
418     // Register for notification when TopSites changes so that we can update
419     // ourself.
420     registrar_.Add(this, chrome::NOTIFICATION_TOP_SITES_CHANGED,
421                    content::Source<history::TopSites>(top_sites_));
422   }
423
424   server_set_root(server_, root_item_);
425 }
426
427 void GlobalMenuBarX11::Disable() {
428   for (CommandIDMenuItemMap::const_iterator it = id_to_menu_item_.begin();
429        it != id_to_menu_item_.end(); ++it) {
430     chrome::RemoveCommandObserver(browser_, it->first, this);
431   }
432   id_to_menu_item_.clear();
433
434   pref_change_registrar_.RemoveAll();
435 }
436
437 DbusmenuMenuitem* GlobalMenuBarX11::BuildStaticMenu(
438     DbusmenuMenuitem* parent,
439     int menu_str_id,
440     GlobalMenuBarCommand* commands) {
441   DbusmenuMenuitem* top = menuitem_new();
442   menuitem_property_set(
443       top, kPropertyLabel,
444       ui::RemoveWindowsStyleAccelerators(
445           l10n_util::GetStringUTF8(menu_str_id)).c_str());
446   menuitem_property_set_bool(top, kPropertyVisible, true);
447
448   for (int i = 0; commands[i].str_id != MENU_END; ++i) {
449     DbusmenuMenuitem* menu_item = NULL;
450     int command_id = commands[i].command;
451     if (commands[i].str_id == MENU_SEPARATOR) {
452       menu_item = BuildSeparator();
453     } else {
454       std::string label = ui::ConvertAcceleratorsFromWindowsStyle(
455           l10n_util::GetStringUTF8(commands[i].str_id));
456
457       menu_item = BuildMenuItem(label, commands[i].tag);
458
459       if (command_id == MENU_DISABLED_ID) {
460         menuitem_property_set_bool(menu_item, kPropertyEnabled, false);
461       } else {
462         if (command_id == IDC_SHOW_BOOKMARK_BAR)
463           menuitem_property_set(menu_item, kPropertyToggleType, kTypeCheckmark);
464
465         id_to_menu_item_.insert(std::make_pair(command_id, menu_item));
466         g_object_set_data(G_OBJECT(menu_item), "command-id",
467                           GINT_TO_POINTER(command_id));
468         g_signal_connect(menu_item, "item-activated",
469                          G_CALLBACK(OnItemActivatedThunk), this);
470       }
471     }
472
473     menuitem_child_append(top, menu_item);
474     g_object_unref(menu_item);
475   }
476
477   menuitem_child_append(parent, top);
478   g_object_unref(top);
479   return top;
480 }
481
482 void GlobalMenuBarX11::RegisterAccelerator(DbusmenuMenuitem* item,
483                                            const ui::Accelerator& accelerator) {
484   // A translation of libdbusmenu-gtk's menuitem_property_set_shortcut()
485   // translated from GDK types to ui::Accelerator types.
486   GVariantBuilder builder;
487   g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);
488
489   if (accelerator.IsCtrlDown())
490     g_variant_builder_add(&builder, "s", "Control");
491   if (accelerator.IsAltDown())
492     g_variant_builder_add(&builder, "s", "Alt");
493   if (accelerator.IsShiftDown())
494     g_variant_builder_add(&builder, "s", "Shift");
495
496   char* name = XKeysymToString(XKeysymForWindowsKeyCode(
497       accelerator.key_code(), false));
498   if (!name) {
499     NOTIMPLEMENTED();
500     return;
501   }
502   g_variant_builder_add(&builder, "s", name);
503
504   GVariant* inside_array = g_variant_builder_end(&builder);
505   g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);
506   g_variant_builder_add_value(&builder, inside_array);
507   GVariant* outside_array = g_variant_builder_end(&builder);
508
509   menuitem_property_set_variant(item, kPropertyShortcut, outside_array);
510 }
511
512 GlobalMenuBarX11::HistoryItem* GlobalMenuBarX11::HistoryItemForTab(
513     const TabRestoreService::Tab& entry) {
514   const sessions::SerializedNavigationEntry& current_navigation =
515       entry.navigations.at(entry.current_navigation_index);
516   HistoryItem* item = new HistoryItem();
517   item->title = current_navigation.title();
518   item->url = current_navigation.virtual_url();
519   item->session_id = entry.id;
520
521   return item;
522 }
523
524 void GlobalMenuBarX11::AddHistoryItemToMenu(HistoryItem* item,
525                                             DbusmenuMenuitem* menu,
526                                             int tag,
527                                             int index) {
528   string16 title = item->title;
529   std::string url_string = item->url.possibly_invalid_spec();
530
531   if (title.empty())
532     title = UTF8ToUTF16(url_string);
533   gfx::ElideString(title, kMaximumMenuWidthInChars, &title);
534
535   DbusmenuMenuitem* menu_item = BuildMenuItem(UTF16ToUTF8(title), tag);
536   g_signal_connect(menu_item, "item-activated",
537                    G_CALLBACK(OnHistoryItemActivatedThunk), this);
538
539   g_object_set_data_full(G_OBJECT(menu_item), kHistoryItem, item,
540                          DeleteHistoryItem);
541   menuitem_child_add_position(menu, menu_item, index);
542   g_object_unref(menu_item);
543 }
544
545 void GlobalMenuBarX11::GetTopSitesData() {
546   DCHECK(top_sites_);
547
548   top_sites_->GetMostVisitedURLs(
549       base::Bind(&GlobalMenuBarX11::OnTopSitesReceived,
550                  weak_ptr_factory_.GetWeakPtr()));
551 }
552
553 void GlobalMenuBarX11::OnTopSitesReceived(
554     const history::MostVisitedURLList& visited_list) {
555   ClearMenuSection(history_menu_, TAG_MOST_VISITED);
556
557   int index = GetIndexOfMenuItemWithTag(history_menu_,
558                                         TAG_MOST_VISITED_HEADER) + 1;
559
560   for (size_t i = 0; i < visited_list.size() && i < kMostVisitedCount; ++i) {
561     const history::MostVisitedURL& visited = visited_list[i];
562     if (visited.url.spec().empty())
563       break;  // This is the signal that there are no more real visited sites.
564
565     HistoryItem* item = new HistoryItem();
566     item->title = visited.title;
567     item->url = visited.url;
568
569     AddHistoryItemToMenu(item,
570                          history_menu_,
571                          TAG_MOST_VISITED,
572                          index++);
573   }
574 }
575
576 void GlobalMenuBarX11::OnBookmarkBarVisibilityChanged() {
577   CommandIDMenuItemMap::iterator it =
578       id_to_menu_item_.find(IDC_SHOW_BOOKMARK_BAR);
579   if (it != id_to_menu_item_.end()) {
580     PrefService* prefs = browser_->profile()->GetPrefs();
581     // Note: Unlike the GTK version, we don't appear to need to do tricks where
582     // we block activation while setting the toggle.
583     menuitem_property_set_int(it->second, kPropertyToggleState,
584                               prefs->GetBoolean(prefs::kShowBookmarkBar));
585   }
586 }
587
588 int GlobalMenuBarX11::GetIndexOfMenuItemWithTag(DbusmenuMenuitem* menu,
589                                                 int tag_id) {
590   GList* childs = menuitem_get_children(menu);
591   int i = 0;
592   for (; childs != NULL; childs = childs->next, i++) {
593     int tag =
594         GPOINTER_TO_INT(g_object_get_data(G_OBJECT(childs->data), kTypeTag));
595     if (tag == tag_id)
596       return i;
597   }
598
599   NOTREACHED();
600   return -1;
601 }
602
603 void GlobalMenuBarX11::ClearMenuSection(DbusmenuMenuitem* menu, int tag_id) {
604   std::vector<DbusmenuMenuitem*> menuitems_to_delete;
605
606   GList* childs = menuitem_get_children(menu);
607   for (; childs != NULL; childs = childs->next) {
608     DbusmenuMenuitem* current_item = reinterpret_cast<DbusmenuMenuitem*>(
609         childs->data);
610     ClearMenuSection(current_item, tag_id);
611
612     int tag =
613         GPOINTER_TO_INT(g_object_get_data(G_OBJECT(childs->data), kTypeTag));
614     if (tag == tag_id)
615       menuitems_to_delete.push_back(current_item);
616   }
617
618   for (std::vector<DbusmenuMenuitem*>::const_iterator it =
619            menuitems_to_delete.begin(); it != menuitems_to_delete.end(); ++it) {
620     menuitem_child_delete(menu, *it);
621   }
622 }
623
624 // static
625 void GlobalMenuBarX11::DeleteHistoryItem(void* void_item) {
626   HistoryItem* item =
627       reinterpret_cast<GlobalMenuBarX11::HistoryItem*>(void_item);
628   delete item;
629 }
630
631 void GlobalMenuBarX11::EnabledStateChangedForCommand(int id, bool enabled) {
632   CommandIDMenuItemMap::iterator it = id_to_menu_item_.find(id);
633   if (it != id_to_menu_item_.end())
634     menuitem_property_set_bool(it->second, kPropertyEnabled, enabled);
635 }
636
637 void GlobalMenuBarX11::Observe(int type,
638                                const content::NotificationSource& source,
639                                const content::NotificationDetails& details) {
640   if (type == chrome::NOTIFICATION_TOP_SITES_CHANGED) {
641     GetTopSitesData();
642   } else {
643     NOTREACHED();
644   }
645 }
646
647 void GlobalMenuBarX11::TabRestoreServiceChanged(TabRestoreService* service) {
648   const TabRestoreService::Entries& entries = service->entries();
649
650   ClearMenuSection(history_menu_, TAG_RECENTLY_CLOSED);
651
652   // We'll get the index the "Recently Closed" header. (This can vary depending
653   // on the number of "Most Visited" items.
654   int index = GetIndexOfMenuItemWithTag(history_menu_,
655                                         TAG_RECENTLY_CLOSED_HEADER) + 1;
656
657   unsigned int added_count = 0;
658   for (TabRestoreService::Entries::const_iterator it = entries.begin();
659        it != entries.end() && added_count < kRecentlyClosedCount; ++it) {
660     TabRestoreService::Entry* entry = *it;
661
662     if (entry->type == TabRestoreService::WINDOW) {
663       TabRestoreService::Window* entry_win =
664           static_cast<TabRestoreService::Window*>(entry);
665       std::vector<TabRestoreService::Tab>& tabs = entry_win->tabs;
666       if (tabs.empty())
667         continue;
668
669       // Create the item for the parent/window.
670       HistoryItem* item = new HistoryItem();
671       item->session_id = entry_win->id;
672
673       std::string title = item->tabs.size() == 1 ?
674           l10n_util::GetStringUTF8(
675               IDS_NEW_TAB_RECENTLY_CLOSED_WINDOW_SINGLE) :
676           l10n_util::GetStringFUTF8(
677               IDS_NEW_TAB_RECENTLY_CLOSED_WINDOW_MULTIPLE,
678               base::IntToString16(item->tabs.size()));
679       DbusmenuMenuitem* parent_item = BuildMenuItem(
680           title, TAG_RECENTLY_CLOSED);
681       menuitem_child_add_position(history_menu_, parent_item, index++);
682       g_object_unref(parent_item);
683
684       // The mac version of this code allows the user to click on the parent
685       // menu item to have the same effect as clicking the restore window
686       // submenu item. GTK+ helpfully activates a menu item when it shows a
687       // submenu so toss that feature out.
688       DbusmenuMenuitem* restore_item = BuildMenuItem(
689           l10n_util::GetStringUTF8(
690               IDS_HISTORY_CLOSED_RESTORE_WINDOW_LINUX).c_str(),
691           TAG_RECENTLY_CLOSED);
692       g_signal_connect(restore_item, "item-activated",
693                        G_CALLBACK(OnHistoryItemActivatedThunk), this);
694       g_object_set_data_full(G_OBJECT(restore_item), kHistoryItem, item,
695                              DeleteHistoryItem);
696       menuitem_child_append(parent_item, restore_item);
697       g_object_unref(restore_item);
698
699       DbusmenuMenuitem* separator = BuildSeparator();
700       menuitem_child_append(parent_item, separator);
701       g_object_unref(separator);
702
703       // Loop over the window's tabs and add them to the submenu.
704       int subindex = 2;
705       std::vector<TabRestoreService::Tab>::const_iterator iter;
706       for (iter = tabs.begin(); iter != tabs.end(); ++iter) {
707         TabRestoreService::Tab tab = *iter;
708         HistoryItem* tab_item = HistoryItemForTab(tab);
709         item->tabs.push_back(tab_item);
710         AddHistoryItemToMenu(tab_item,
711                              parent_item,
712                              TAG_RECENTLY_CLOSED,
713                              subindex++);
714       }
715
716       ++added_count;
717     } else if (entry->type == TabRestoreService::TAB) {
718       TabRestoreService::Tab* tab = static_cast<TabRestoreService::Tab*>(entry);
719       HistoryItem* item = HistoryItemForTab(*tab);
720       AddHistoryItemToMenu(item,
721                            history_menu_,
722                            TAG_RECENTLY_CLOSED,
723                            index++);
724       ++added_count;
725     }
726   }
727 }
728
729 void GlobalMenuBarX11::TabRestoreServiceDestroyed(
730     TabRestoreService* service) {
731   tab_restore_service_ = NULL;
732 }
733
734 void GlobalMenuBarX11::OnWindowMapped(unsigned long xid) {
735   if (!server_)
736     InitServer(xid);
737
738   GlobalMenuBarRegistrarX11::GetInstance()->OnWindowMapped(xid);
739 }
740
741 void GlobalMenuBarX11::OnWindowUnmapped(unsigned long xid) {
742   GlobalMenuBarRegistrarX11::GetInstance()->OnWindowUnmapped(xid);
743 }
744
745 void GlobalMenuBarX11::OnItemActivated(DbusmenuMenuitem* item,
746                                        unsigned int timestamp) {
747   int id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item), "command-id"));
748   chrome::ExecuteCommand(browser_, id);
749 }
750
751 void GlobalMenuBarX11::OnHistoryItemActivated(DbusmenuMenuitem* sender,
752                                               unsigned int timestamp) {
753   // Note: We don't have access to the event modifiers used to click the menu
754   // item since that happens in a different process.
755   HistoryItem* item = reinterpret_cast<HistoryItem*>(
756       g_object_get_data(G_OBJECT(sender), kHistoryItem));
757
758   // If this item can be restored using TabRestoreService, do so. Otherwise,
759   // just load the URL.
760   TabRestoreService* service =
761       TabRestoreServiceFactory::GetForProfile(profile_);
762   if (item->session_id && service) {
763     service->RestoreEntryById(browser_->tab_restore_service_delegate(),
764                               item->session_id, browser_->host_desktop_type(),
765                               UNKNOWN);
766   } else {
767     DCHECK(item->url.is_valid());
768     browser_->OpenURL(content::OpenURLParams(
769         item->url,
770         content::Referrer(),
771         NEW_FOREGROUND_TAB,
772         content::PAGE_TRANSITION_AUTO_BOOKMARK,
773         false));
774   }
775 }
776
777 void GlobalMenuBarX11::OnHistoryMenuAboutToShow(DbusmenuMenuitem* item) {
778   if (!tab_restore_service_) {
779     tab_restore_service_ = TabRestoreServiceFactory::GetForProfile(profile_);
780     if (tab_restore_service_) {
781       tab_restore_service_->LoadTabsFromLastSession();
782       tab_restore_service_->AddObserver(this);
783
784       // If LoadTabsFromLastSession doesn't load tabs, it won't call
785       // TabRestoreServiceChanged(). This ensures that all new windows after
786       // the first one will have their menus populated correctly.
787       TabRestoreServiceChanged(tab_restore_service_);
788     }
789   }
790 }