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"
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
,
60 typedef DbusmenuMenuitem
* (*dbusmenu_menuitem_property_set_variant_func
)(
61 DbusmenuMenuitem
* item
,
64 typedef DbusmenuMenuitem
* (*dbusmenu_menuitem_property_set_bool_func
)(
65 DbusmenuMenuitem
* item
,
68 typedef DbusmenuMenuitem
* (*dbusmenu_menuitem_property_set_int_func
)(
69 DbusmenuMenuitem
* item
,
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
{
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
=
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
},
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;
259 attempted_load
= true;
261 void* dbusmenu_lib
= dlopen("libdbusmenu-glib.so", RTLD_LAZY
);
263 dbusmenu_lib
= dlopen("libdbusmenu-glib.so.4", RTLD_LAZY
);
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"));
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.
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
;
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
),
333 history_menu_(nullptr),
334 profiles_menu_(nullptr),
336 tab_restore_service_(nullptr),
337 scoped_observer_(this),
338 weak_ptr_factory_(this) {
339 EnsureMethodsLoaded();
342 host_
->AddObserver(this);
345 GlobalMenuBarX11::~GlobalMenuBarX11() {
349 if (tab_restore_service_
)
350 tab_restore_service_
->RemoveObserver(this);
352 g_object_unref(server_
);
353 host_
->RemoveObserver(this);
355 BrowserList::RemoveObserver(this);
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);
370 DbusmenuMenuitem
* GlobalMenuBarX11::BuildMenuItem(
371 const std::string
& label
,
373 DbusmenuMenuitem
* item
= menuitem_new();
374 menuitem_property_set(item
, kPropertyLabel
, label
.c_str());
375 menuitem_property_set_bool(item
, kPropertyVisible
, true);
378 g_object_set_data(G_OBJECT(item
), kTypeTag
, GINT_TO_POINTER(tag_id
));
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_
);
434 // Register as TopSitesObserver so that we can update ourselves when the
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
,
464 GlobalMenuBarCommand
* commands
) {
465 DbusmenuMenuitem
* top
= menuitem_new();
466 menuitem_property_set(
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();
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);
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
);
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));
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
;
548 void GlobalMenuBarX11::AddHistoryItemToMenu(HistoryItem
* item
,
549 DbusmenuMenuitem
* menu
,
552 base::string16 title
= item
->title
;
553 std::string url_string
= item
->url
.possibly_invalid_spec();
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
,
565 menuitem_child_add_position(menu
, menu_item
, index
);
566 g_object_unref(menu_item
);
569 void GlobalMenuBarX11::GetTopSitesData() {
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
,
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(
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
);
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
),
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);
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
,
673 GList
* childs
= menuitem_get_children(menu
);
675 for (; childs
!= nullptr; childs
= childs
->next
, i
++) {
677 GPOINTER_TO_INT(g_object_get_data(G_OBJECT(childs
->data
), kTypeTag
));
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
*>(
693 ClearMenuSection(current_item
, tag_id
);
696 GPOINTER_TO_INT(g_object_get_data(G_OBJECT(childs
->data
), kTypeTag
));
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
);
708 void GlobalMenuBarX11::DeleteHistoryItem(void* void_item
) {
710 reinterpret_cast<GlobalMenuBarX11::HistoryItem
*>(void_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
) {
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
=
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
;
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
,
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.
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
,
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
,
820 void GlobalMenuBarX11::TabRestoreServiceDestroyed(
821 sessions::TabRestoreService
* service
) {
822 tab_restore_service_
= nullptr;
825 void GlobalMenuBarX11::OnWindowMapped(unsigned long 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(),
858 DCHECK(item
->url
.is_valid());
859 browser_
->OpenURL(content::OpenURLParams(
863 ui::PAGE_TRANSITION_AUTO_BOOKMARK
,
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
);