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