Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / ui / views / frame / global_menu_bar_x11.cc
blob8968cb0aa535fb3898a1215a7fc8670237508811
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/history/top_sites_factory.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/profiles/profile_info_cache.h"
22 #include "chrome/browser/profiles/profile_manager.h"
23 #include "chrome/browser/profiles/profile_window.h"
24 #include "chrome/browser/sessions/tab_restore_service_factory.h"
25 #include "chrome/browser/ui/browser.h"
26 #include "chrome/browser/ui/browser_commands.h"
27 #include "chrome/browser/ui/browser_list.h"
28 #include "chrome/browser/ui/browser_tab_restore_service_delegate.h"
29 #include "chrome/browser/ui/views/frame/browser_desktop_window_tree_host_x11.h"
30 #include "chrome/browser/ui/views/frame/browser_view.h"
31 #include "chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h"
32 #include "chrome/common/pref_names.h"
33 #include "chrome/grit/generated_resources.h"
34 #include "components/history/core/browser/top_sites.h"
35 #include "components/sessions/core/tab_restore_service.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 = nullptr;
91 dbusmenu_menuitem_get_children_func menuitem_get_children = nullptr;
92 dbusmenu_menuitem_child_add_position_func menuitem_child_add_position = nullptr;
93 dbusmenu_menuitem_child_append_func menuitem_child_append = nullptr;
94 dbusmenu_menuitem_child_delete_func menuitem_child_delete = nullptr;
95 dbusmenu_menuitem_property_set_func menuitem_property_set = nullptr;
96 dbusmenu_menuitem_property_set_variant_func menuitem_property_set_variant =
97 nullptr;
98 dbusmenu_menuitem_property_set_bool_func menuitem_property_set_bool = nullptr;
99 dbusmenu_menuitem_property_set_int_func menuitem_property_set_int = nullptr;
101 // DbusmenuServer methods:
102 dbusmenu_server_new_func server_new = nullptr;
103 dbusmenu_server_set_root_func server_set_root = nullptr;
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_(nullptr),
332 root_item_(nullptr),
333 history_menu_(nullptr),
334 profiles_menu_(nullptr),
335 top_sites_(nullptr),
336 tab_restore_service_(nullptr),
337 scoped_observer_(this),
338 weak_ptr_factory_(this) {
339 EnsureMethodsLoaded();
341 if (server_new)
342 host_->AddObserver(this);
345 GlobalMenuBarX11::~GlobalMenuBarX11() {
346 if (server_) {
347 Disable();
349 if (tab_restore_service_)
350 tab_restore_service_->RemoveObserver(this);
352 g_object_unref(server_);
353 host_->RemoveObserver(this);
355 BrowserList::RemoveObserver(this);
358 // static
359 std::string GlobalMenuBarX11::GetPathForWindow(unsigned long xid) {
360 return base::StringPrintf("/com/canonical/menu/%lX", xid);
363 DbusmenuMenuitem* GlobalMenuBarX11::BuildSeparator() {
364 DbusmenuMenuitem* item = menuitem_new();
365 menuitem_property_set(item, kPropertyType, kTypeSeparator);
366 menuitem_property_set_bool(item, kPropertyVisible, true);
367 return item;
370 DbusmenuMenuitem* GlobalMenuBarX11::BuildMenuItem(
371 const std::string& label,
372 int tag_id) {
373 DbusmenuMenuitem* item = menuitem_new();
374 menuitem_property_set(item, kPropertyLabel, label.c_str());
375 menuitem_property_set_bool(item, kPropertyVisible, true);
377 if (tag_id)
378 g_object_set_data(G_OBJECT(item), kTypeTag, GINT_TO_POINTER(tag_id));
380 return item;
383 void GlobalMenuBarX11::InitServer(unsigned long xid) {
384 std::string path = GetPathForWindow(xid);
386 ANNOTATE_SCOPED_MEMORY_LEAK; // http://crbug.com/314087
387 server_ = server_new(path.c_str());
390 root_item_ = menuitem_new();
391 menuitem_property_set(root_item_, kPropertyLabel, "Root");
392 menuitem_property_set_bool(root_item_, kPropertyVisible, true);
394 // First build static menu content.
395 BuildStaticMenu(root_item_, IDS_FILE_MENU_LINUX, file_menu);
396 BuildStaticMenu(root_item_, IDS_EDIT_MENU_LINUX, edit_menu);
397 BuildStaticMenu(root_item_, IDS_VIEW_MENU_LINUX, view_menu);
398 history_menu_ = BuildStaticMenu(
399 root_item_, IDS_HISTORY_MENU_LINUX, history_menu);
400 BuildStaticMenu(root_item_, IDS_TOOLS_MENU_LINUX, tools_menu);
401 profiles_menu_ = BuildStaticMenu(
402 root_item_, IDS_PROFILES_OPTIONS_GROUP_NAME, profiles_menu);
403 BuildStaticMenu(root_item_, IDS_HELP_MENU_LINUX, help_menu);
405 // We have to connect to |history_menu_item|'s "activate" signal instead of
406 // |history_menu|'s "show" signal because we are not supposed to modify the
407 // menu during "show"
408 g_signal_connect(history_menu_, "about-to-show",
409 G_CALLBACK(OnHistoryMenuAboutToShowThunk), this);
411 for (CommandIDMenuItemMap::const_iterator it = id_to_menu_item_.begin();
412 it != id_to_menu_item_.end(); ++it) {
413 menuitem_property_set_bool(it->second, kPropertyEnabled,
414 chrome::IsCommandEnabled(browser_, it->first));
416 ui::Accelerator accelerator;
417 if (browser_view_->GetAccelerator(it->first, &accelerator))
418 RegisterAccelerator(it->second, accelerator);
420 chrome::AddCommandObserver(browser_, it->first, this);
423 pref_change_registrar_.Init(browser_->profile()->GetPrefs());
424 pref_change_registrar_.Add(
425 bookmarks::prefs::kShowBookmarkBar,
426 base::Bind(&GlobalMenuBarX11::OnBookmarkBarVisibilityChanged,
427 base::Unretained(this)));
428 OnBookmarkBarVisibilityChanged();
430 top_sites_ = TopSitesFactory::GetForProfile(profile_);
431 if (top_sites_) {
432 GetTopSitesData();
434 // Register as TopSitesObserver so that we can update ourselves when the
435 // TopSites changes.
436 scoped_observer_.Add(top_sites_.get());
439 ProfileManager* profile_manager = g_browser_process->profile_manager();
440 DCHECK(profile_manager);
441 avatar_menu_.reset(new AvatarMenu(
442 &profile_manager->GetProfileInfoCache(), this, nullptr));
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 = nullptr;
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 sessions::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 != nullptr; 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 != nullptr; 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::TopSitesLoaded(history::TopSites* top_sites) {
734 void GlobalMenuBarX11::TopSitesChanged(history::TopSites* top_sites,
735 ChangeReason change_reason) {
736 GetTopSitesData();
739 void GlobalMenuBarX11::TabRestoreServiceChanged(
740 sessions::TabRestoreService* service) {
741 const sessions::TabRestoreService::Entries& entries = service->entries();
743 ClearMenuSection(history_menu_, TAG_RECENTLY_CLOSED);
745 // We'll get the index the "Recently Closed" header. (This can vary depending
746 // on the number of "Most Visited" items.
747 int index = GetIndexOfMenuItemWithTag(history_menu_,
748 TAG_RECENTLY_CLOSED_HEADER) + 1;
750 unsigned int added_count = 0;
751 for (sessions::TabRestoreService::Entries::const_iterator it =
752 entries.begin();
753 it != entries.end() && added_count < kRecentlyClosedCount; ++it) {
754 sessions::TabRestoreService::Entry* entry = *it;
756 if (entry->type == sessions::TabRestoreService::WINDOW) {
757 sessions::TabRestoreService::Window* entry_win =
758 static_cast<sessions::TabRestoreService::Window*>(entry);
759 std::vector<sessions::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 = l10n_util::GetPluralStringFUTF8(
768 IDS_RECENTLY_CLOSED_WINDOW, tabs.size());
769 DbusmenuMenuitem* parent_item = BuildMenuItem(
770 title, TAG_RECENTLY_CLOSED);
771 menuitem_child_add_position(history_menu_, parent_item, index++);
772 g_object_unref(parent_item);
774 // The mac version of this code allows the user to click on the parent
775 // menu item to have the same effect as clicking the restore window
776 // submenu item. GTK+ helpfully activates a menu item when it shows a
777 // submenu so toss that feature out.
778 DbusmenuMenuitem* restore_item = BuildMenuItem(
779 l10n_util::GetStringUTF8(
780 IDS_HISTORY_CLOSED_RESTORE_WINDOW_LINUX).c_str(),
781 TAG_RECENTLY_CLOSED);
782 g_signal_connect(restore_item, "item-activated",
783 G_CALLBACK(OnHistoryItemActivatedThunk), this);
784 g_object_set_data_full(G_OBJECT(restore_item), kHistoryItem, item,
785 DeleteHistoryItem);
786 menuitem_child_append(parent_item, restore_item);
787 g_object_unref(restore_item);
789 DbusmenuMenuitem* separator = BuildSeparator();
790 menuitem_child_append(parent_item, separator);
791 g_object_unref(separator);
793 // Loop over the window's tabs and add them to the submenu.
794 int subindex = 2;
795 std::vector<sessions::TabRestoreService::Tab>::const_iterator iter;
796 for (iter = tabs.begin(); iter != tabs.end(); ++iter) {
797 sessions::TabRestoreService::Tab tab = *iter;
798 HistoryItem* tab_item = HistoryItemForTab(tab);
799 item->tabs.push_back(tab_item);
800 AddHistoryItemToMenu(tab_item,
801 parent_item,
802 TAG_RECENTLY_CLOSED,
803 subindex++);
806 ++added_count;
807 } else if (entry->type == sessions::TabRestoreService::TAB) {
808 sessions::TabRestoreService::Tab* tab =
809 static_cast<sessions::TabRestoreService::Tab*>(entry);
810 HistoryItem* item = HistoryItemForTab(*tab);
811 AddHistoryItemToMenu(item,
812 history_menu_,
813 TAG_RECENTLY_CLOSED,
814 index++);
815 ++added_count;
820 void GlobalMenuBarX11::TabRestoreServiceDestroyed(
821 sessions::TabRestoreService* service) {
822 tab_restore_service_ = nullptr;
825 void GlobalMenuBarX11::OnWindowMapped(unsigned long xid) {
826 if (!server_)
827 InitServer(xid);
829 GlobalMenuBarRegistrarX11::GetInstance()->OnWindowMapped(xid);
832 void GlobalMenuBarX11::OnWindowUnmapped(unsigned long xid) {
833 GlobalMenuBarRegistrarX11::GetInstance()->OnWindowUnmapped(xid);
836 void GlobalMenuBarX11::OnItemActivated(DbusmenuMenuitem* item,
837 unsigned int timestamp) {
838 int id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item), "command-id"));
839 chrome::ExecuteCommand(browser_, id);
842 void GlobalMenuBarX11::OnHistoryItemActivated(DbusmenuMenuitem* sender,
843 unsigned int timestamp) {
844 // Note: We don't have access to the event modifiers used to click the menu
845 // item since that happens in a different process.
846 HistoryItem* item = reinterpret_cast<HistoryItem*>(
847 g_object_get_data(G_OBJECT(sender), kHistoryItem));
849 // If this item can be restored using TabRestoreService, do so. Otherwise,
850 // just load the URL.
851 sessions::TabRestoreService* service =
852 TabRestoreServiceFactory::GetForProfile(profile_);
853 if (item->session_id && service) {
854 service->RestoreEntryById(browser_->tab_restore_service_delegate(),
855 item->session_id, browser_->host_desktop_type(),
856 UNKNOWN);
857 } else {
858 DCHECK(item->url.is_valid());
859 browser_->OpenURL(content::OpenURLParams(
860 item->url,
861 content::Referrer(),
862 NEW_FOREGROUND_TAB,
863 ui::PAGE_TRANSITION_AUTO_BOOKMARK,
864 false));
868 void GlobalMenuBarX11::OnHistoryMenuAboutToShow(DbusmenuMenuitem* item) {
869 if (!tab_restore_service_) {
870 tab_restore_service_ = TabRestoreServiceFactory::GetForProfile(profile_);
871 if (tab_restore_service_) {
872 tab_restore_service_->LoadTabsFromLastSession();
873 tab_restore_service_->AddObserver(this);
875 // If LoadTabsFromLastSession doesn't load tabs, it won't call
876 // TabRestoreServiceChanged(). This ensures that all new windows after
877 // the first one will have their menus populated correctly.
878 TabRestoreServiceChanged(tab_restore_service_);
883 void GlobalMenuBarX11::OnProfileItemActivated(DbusmenuMenuitem* sender,
884 unsigned int timestamp) {
885 int id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(sender), kProfileId));
886 avatar_menu_->SwitchToProfile(id, false, ProfileMetrics::SWITCH_PROFILE_MENU);
889 void GlobalMenuBarX11::OnEditProfileItemActivated(DbusmenuMenuitem* sender,
890 unsigned int timestamp) {
891 int id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(sender), kProfileId));
892 avatar_menu_->EditProfile(id);
895 void GlobalMenuBarX11::OnCreateProfileItemActivated(DbusmenuMenuitem* sender,
896 unsigned int timestamp) {
897 profiles::CreateAndSwitchToNewProfile(chrome::HOST_DESKTOP_TYPE_NATIVE,
898 ProfileManager::CreateCallback(),
899 ProfileMetrics::ADD_NEW_USER_MENU);