Adding instrumentation to locate the source of jankiness
[chromium-blink-merge.git] / chrome / browser / ui / views / frame / global_menu_bar_x11.cc
blob7671115cb6591d3cb429e045eeab96b345b5a330
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.
5 #include "chrome/browser/ui/views/frame/global_menu_bar_x11.h"
7 #include <dlfcn.h>
8 #include <glib-object.h>
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"
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);
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);
78 // A line in the static menu definitions.
79 struct GlobalMenuBarCommand {
80 int str_id;
81 int command;
82 int tag;
85 namespace {
87 // Retrieved functions from libdbusmenu-glib.
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;
101 // DbusmenuServer methods:
102 dbusmenu_server_new_func server_new = NULL;
103 dbusmenu_server_set_root_func server_set_root = NULL;
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";
114 const char kTypeCheckmark[] = "checkmark";
115 const char kTypeSeparator[] = "separator";
117 // Data set on GObjectgs.
118 const char kTypeTag[] = "type-tag";
119 const char kHistoryItem[] = "history-item";
120 const char kProfileId[] = "profile-id";
122 // The maximum number of most visited items to display.
123 const unsigned int kMostVisitedCount = 8;
125 // The number of recently closed items to get.
126 const unsigned int kRecentlyClosedCount = 8;
128 // Menus more than this many chars long will get trimmed.
129 const int kMaximumMenuWidthInChars = 50;
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;
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;
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 },
151 { MENU_SEPARATOR, MENU_SEPARATOR },
153 { IDS_CREATE_SHORTCUTS, IDC_CREATE_SHORTCUTS },
155 { MENU_SEPARATOR, MENU_SEPARATOR },
157 { IDS_CLOSE_WINDOW_LINUX, IDC_CLOSE_WINDOW },
158 { IDS_CLOSE_TAB_LINUX, IDC_CLOSE_TAB },
159 { IDS_SAVE_PAGE, IDC_SAVE_PAGE },
161 { MENU_SEPARATOR, MENU_SEPARATOR },
163 { IDS_PRINT, IDC_PRINT },
165 { MENU_END, MENU_END }
168 GlobalMenuBarCommand edit_menu[] = {
169 { IDS_CUT, IDC_CUT },
170 { IDS_COPY, IDC_COPY },
171 { IDS_PASTE, IDC_PASTE },
173 { MENU_SEPARATOR, MENU_SEPARATOR },
175 { IDS_FIND, IDC_FIND },
177 { MENU_SEPARATOR, MENU_SEPARATOR },
179 { IDS_PREFERENCES, IDC_OPTIONS },
181 { MENU_END, MENU_END }
184 GlobalMenuBarCommand view_menu[] = {
185 { IDS_SHOW_BOOKMARK_BAR, IDC_SHOW_BOOKMARK_BAR },
187 { MENU_SEPARATOR, MENU_SEPARATOR },
189 { IDS_STOP_MENU_LINUX, IDC_STOP },
190 { IDS_RELOAD_MENU_LINUX, IDC_RELOAD },
192 { MENU_SEPARATOR, MENU_SEPARATOR },
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 },
199 { MENU_END, MENU_END }
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 },
207 { MENU_SEPARATOR, MENU_SEPARATOR },
209 { IDS_HISTORY_VISITED_LINUX, MENU_DISABLED_ID, TAG_MOST_VISITED_HEADER },
211 { MENU_SEPARATOR, MENU_SEPARATOR },
213 { IDS_HISTORY_CLOSED_LINUX, MENU_DISABLED_ID, TAG_RECENTLY_CLOSED_HEADER },
215 { MENU_SEPARATOR, MENU_SEPARATOR },
217 { IDS_SHOWFULLHISTORY_LINK, IDC_SHOW_HISTORY },
219 { MENU_END, MENU_END }
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 },
227 { MENU_SEPARATOR, MENU_SEPARATOR },
229 { IDS_TASK_MANAGER, IDC_TASK_MANAGER },
230 { IDS_CLEAR_BROWSING_DATA, IDC_CLEAR_BROWSING_DATA },
232 { MENU_SEPARATOR, MENU_SEPARATOR },
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 },
239 { MENU_END, MENU_END }
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 }
250 GlobalMenuBarCommand profiles_menu[] = {
251 { MENU_SEPARATOR, MENU_SEPARATOR },
252 { MENU_END, MENU_END }
255 void EnsureMethodsLoaded() {
256 static bool attempted_load = false;
257 if (attempted_load)
258 return;
259 attempted_load = true;
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;
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"));
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"));
298 } // namespace
300 struct GlobalMenuBarX11::HistoryItem {
301 HistoryItem() : session_id(0) {}
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;
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;
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;
321 private:
322 DISALLOW_COPY_AND_ASSIGN(HistoryItem);
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();
340 if (server_new)
341 host_->AddObserver(this);
344 GlobalMenuBarX11::~GlobalMenuBarX11() {
345 if (server_) {
346 Disable();
348 if (tab_restore_service_)
349 tab_restore_service_->RemoveObserver(this);
351 g_object_unref(server_);
352 host_->RemoveObserver(this);
354 BrowserList::RemoveObserver(this);
357 // static
358 std::string GlobalMenuBarX11::GetPathForWindow(unsigned long xid) {
359 return base::StringPrintf("/com/canonical/menu/%lX", xid);
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;
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);
376 if (tag_id)
377 g_object_set_data(G_OBJECT(item), kTypeTag, GINT_TO_POINTER(tag_id));
379 return item;
382 void GlobalMenuBarX11::InitServer(unsigned long xid) {
383 std::string path = GetPathForWindow(xid);
385 ANNOTATE_SCOPED_MEMORY_LEAK; // http://crbug.com/314087
386 server_ = server_new(path.c_str());
389 root_item_ = menuitem_new();
390 menuitem_property_set(root_item_, kPropertyLabel, "Root");
391 menuitem_property_set_bool(root_item_, kPropertyVisible, true);
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);
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);
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));
415 ui::Accelerator accelerator;
416 if (browser_view_->GetAccelerator(it->first, &accelerator))
417 RegisterAccelerator(it->second, accelerator);
419 chrome::AddCommandObserver(browser_, it->first, this);
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();
429 top_sites_ = profile_->GetTopSites();
430 if (top_sites_) {
431 GetTopSitesData();
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_));
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);
446 RebuildProfilesMenu();
448 server_set_root(server_, root_item_);
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);
456 id_to_menu_item_.clear();
458 pref_change_registrar_.RemoveAll();
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);
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));
481 menu_item = BuildMenuItem(label, commands[i].tag);
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);
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);
497 menuitem_child_append(top, menu_item);
498 g_object_unref(menu_item);
501 menuitem_child_append(parent, top);
502 g_object_unref(top);
503 return top;
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);
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");
520 char* name = XKeysymToString(XKeysymForWindowsKeyCode(
521 accelerator.key_code(), false));
522 if (!name) {
523 NOTIMPLEMENTED();
524 return;
526 g_variant_builder_add(&builder, "s", name);
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);
533 menuitem_property_set_variant(item, kPropertyShortcut, outside_array);
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;
545 return item;
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();
555 if (title.empty())
556 title = base::UTF8ToUTF16(url_string);
557 gfx::ElideString(title, kMaximumMenuWidthInChars, &title);
559 DbusmenuMenuitem* menu_item = BuildMenuItem(base::UTF16ToUTF8(title), tag);
560 g_signal_connect(menu_item, "item-activated",
561 G_CALLBACK(OnHistoryItemActivatedThunk), this);
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);
569 void GlobalMenuBarX11::GetTopSitesData() {
570 DCHECK(top_sites_);
572 top_sites_->GetMostVisitedURLs(
573 base::Bind(&GlobalMenuBarX11::OnTopSitesReceived,
574 weak_ptr_factory_.GetWeakPtr()), false);
577 void GlobalMenuBarX11::OnTopSitesReceived(
578 const history::MostVisitedURLList& visited_list) {
579 ClearMenuSection(history_menu_, TAG_MOST_VISITED);
581 int index = GetIndexOfMenuItemWithTag(history_menu_,
582 TAG_MOST_VISITED_HEADER) + 1;
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.
589 HistoryItem* item = new HistoryItem();
590 item->title = visited.title;
591 item->url = visited.url;
593 AddHistoryItemToMenu(item,
594 history_menu_,
595 TAG_MOST_VISITED,
596 index++);
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));
614 void GlobalMenuBarX11::RebuildProfilesMenu() {
615 ClearMenuSection(profiles_menu_, TAG_PROFILES);
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;
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);
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);
635 if (item.active)
636 active_profile_index = i;
638 menuitem_child_add_position(profiles_menu_, menu_item, i);
639 g_object_unref(menu_item);
642 // There is a separator between the list of profiles and the possible actions.
643 int index = avatar_menu_->GetNumberOfItems() + 1;
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);
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);
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);
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;
682 NOTREACHED();
683 return -1;
686 void GlobalMenuBarX11::ClearMenuSection(DbusmenuMenuitem* menu, int tag_id) {
687 std::vector<DbusmenuMenuitem*> menuitems_to_delete;
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);
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);
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);
707 // static
708 void GlobalMenuBarX11::DeleteHistoryItem(void* void_item) {
709 HistoryItem* item =
710 reinterpret_cast<GlobalMenuBarX11::HistoryItem*>(void_item);
711 delete item;
714 void GlobalMenuBarX11::OnAvatarMenuChanged(AvatarMenu* avatar_menu) {
715 RebuildProfilesMenu();
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();
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);
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();
741 void GlobalMenuBarX11::TabRestoreServiceChanged(TabRestoreService* service) {
742 const TabRestoreService::Entries& entries = service->entries();
744 ClearMenuSection(history_menu_, TAG_RECENTLY_CLOSED);
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;
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;
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;
763 // Create the item for the parent/window.
764 HistoryItem* item = new HistoryItem();
765 item->session_id = entry_win->id;
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);
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);
793 DbusmenuMenuitem* separator = BuildSeparator();
794 menuitem_child_append(parent_item, separator);
795 g_object_unref(separator);
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++);
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;
823 void GlobalMenuBarX11::TabRestoreServiceDestroyed(
824 TabRestoreService* service) {
825 tab_restore_service_ = NULL;
828 void GlobalMenuBarX11::OnWindowMapped(unsigned long xid) {
829 if (!server_)
830 InitServer(xid);
832 GlobalMenuBarRegistrarX11::GetInstance()->OnWindowMapped(xid);
835 void GlobalMenuBarX11::OnWindowUnmapped(unsigned long xid) {
836 GlobalMenuBarRegistrarX11::GetInstance()->OnWindowUnmapped(xid);
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);
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));
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));
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);
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_);
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);
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);
898 void GlobalMenuBarX11::OnCreateProfileItemActivated(DbusmenuMenuitem* sender,
899 unsigned int timestamp) {
900 avatar_menu_->AddNewProfile(ProfileMetrics::ADD_NEW_USER_MENU);