// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/ui/views/frame/global_menu_bar_x11.h" #include #include #include "base/debug/leak_annotations.h" #include "base/logging.h" #include "base/prefs/pref_service.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "chrome/app/chrome_command_ids.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/history/top_sites.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/sessions/tab_restore_service.h" #include "chrome/browser/sessions/tab_restore_service_factory.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" #include "chrome/browser/ui/browser_tab_restore_service_delegate.h" #include "chrome/browser/ui/views/frame/browser_desktop_window_tree_host_x11.h" #include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h" #include "chrome/common/pref_names.h" #include "content/public/browser/notification_source.h" #include "grit/generated_resources.h" #include "ui/base/accelerators/menu_label_accelerator_util_linux.h" #include "ui/base/l10n/l10n_util.h" #include "ui/events/keycodes/keyboard_code_conversion_x.h" #include "ui/gfx/text_elider.h" // libdbusmenu-glib types typedef struct _DbusmenuMenuitem DbusmenuMenuitem; typedef DbusmenuMenuitem* (*dbusmenu_menuitem_new_func)(); typedef bool (*dbusmenu_menuitem_child_add_position_func)( DbusmenuMenuitem* parent, DbusmenuMenuitem* child, unsigned int position); typedef DbusmenuMenuitem* (*dbusmenu_menuitem_child_append_func)( DbusmenuMenuitem* parent, DbusmenuMenuitem* child); typedef bool (*dbusmenu_menuitem_child_delete_func)( DbusmenuMenuitem* parent, DbusmenuMenuitem* child); typedef GList* (*dbusmenu_menuitem_get_children_func)( DbusmenuMenuitem* item); typedef DbusmenuMenuitem* (*dbusmenu_menuitem_property_set_func)( DbusmenuMenuitem* item, const char* property, const char* value); typedef DbusmenuMenuitem* (*dbusmenu_menuitem_property_set_variant_func)( DbusmenuMenuitem* item, const char* property, GVariant* value); typedef DbusmenuMenuitem* (*dbusmenu_menuitem_property_set_bool_func)( DbusmenuMenuitem* item, const char* property, bool value); typedef DbusmenuMenuitem* (*dbusmenu_menuitem_property_set_int_func)( DbusmenuMenuitem* item, const char* property, int value); typedef struct _DbusmenuServer DbusmenuServer; typedef DbusmenuServer* (*dbusmenu_server_new_func)(const char* object); typedef void (*dbusmenu_server_set_root_func)(DbusmenuServer* self, DbusmenuMenuitem* root); // A line in the static menu definitions. struct GlobalMenuBarCommand { int str_id; int command; int tag; }; namespace { // Retrieved functions from libdbusmenu-glib. // DbusmenuMenuItem methods: dbusmenu_menuitem_new_func menuitem_new = NULL; dbusmenu_menuitem_get_children_func menuitem_get_children = NULL; dbusmenu_menuitem_child_add_position_func menuitem_child_add_position = NULL; dbusmenu_menuitem_child_append_func menuitem_child_append = NULL; dbusmenu_menuitem_child_delete_func menuitem_child_delete = NULL; dbusmenu_menuitem_property_set_func menuitem_property_set = NULL; dbusmenu_menuitem_property_set_variant_func menuitem_property_set_variant = NULL; dbusmenu_menuitem_property_set_bool_func menuitem_property_set_bool = NULL; dbusmenu_menuitem_property_set_int_func menuitem_property_set_int = NULL; // DbusmenuServer methods: dbusmenu_server_new_func server_new = NULL; dbusmenu_server_set_root_func server_set_root = NULL; // Properties that we set on menu items: const char kPropertyEnabled[] = "enabled"; const char kPropertyLabel[] = "label"; const char kPropertyShortcut[] = "shortcut"; const char kPropertyType[] = "type"; const char kPropertyToggleType[] = "toggle-type"; const char kPropertyToggleState[] = "toggle-state"; const char kPropertyVisible[] = "visible"; const char kTypeCheckmark[] = "checkmark"; const char kTypeSeparator[] = "separator"; // Data set on GObjectgs. const char kTypeTag[] = "type-tag"; const char kHistoryItem[] = "history-item"; // The maximum number of most visited items to display. const unsigned int kMostVisitedCount = 8; // The number of recently closed items to get. const unsigned int kRecentlyClosedCount = 8; // Menus more than this many chars long will get trimmed. const int kMaximumMenuWidthInChars = 50; // Constants used in menu definitions. const int MENU_SEPARATOR =-1; const int MENU_END = -2; const int MENU_DISABLED_ID = -3; // These tag values are used to refer to menu items. const int TAG_MOST_VISITED = 1; const int TAG_RECENTLY_CLOSED = 2; const int TAG_MOST_VISITED_HEADER = 3; const int TAG_RECENTLY_CLOSED_HEADER = 4; GlobalMenuBarCommand file_menu[] = { { IDS_NEW_TAB, IDC_NEW_TAB }, { IDS_NEW_WINDOW, IDC_NEW_WINDOW }, { IDS_NEW_INCOGNITO_WINDOW, IDC_NEW_INCOGNITO_WINDOW }, { IDS_REOPEN_CLOSED_TABS_LINUX, IDC_RESTORE_TAB }, { IDS_OPEN_FILE_LINUX, IDC_OPEN_FILE }, { IDS_OPEN_LOCATION_LINUX, IDC_FOCUS_LOCATION }, { MENU_SEPARATOR, MENU_SEPARATOR }, { IDS_CREATE_SHORTCUTS, IDC_CREATE_SHORTCUTS }, { MENU_SEPARATOR, MENU_SEPARATOR }, { IDS_CLOSE_WINDOW_LINUX, IDC_CLOSE_WINDOW }, { IDS_CLOSE_TAB_LINUX, IDC_CLOSE_TAB }, { IDS_SAVE_PAGE, IDC_SAVE_PAGE }, { MENU_SEPARATOR, MENU_SEPARATOR }, { IDS_PRINT, IDC_PRINT }, { MENU_END, MENU_END } }; GlobalMenuBarCommand edit_menu[] = { { IDS_CUT, IDC_CUT }, { IDS_COPY, IDC_COPY }, { IDS_PASTE, IDC_PASTE }, { MENU_SEPARATOR, MENU_SEPARATOR }, { IDS_FIND, IDC_FIND }, { MENU_SEPARATOR, MENU_SEPARATOR }, { IDS_PREFERENCES, IDC_OPTIONS }, { MENU_END, MENU_END } }; GlobalMenuBarCommand view_menu[] = { { IDS_SHOW_BOOKMARK_BAR, IDC_SHOW_BOOKMARK_BAR }, { MENU_SEPARATOR, MENU_SEPARATOR }, { IDS_STOP_MENU_LINUX, IDC_STOP }, { IDS_RELOAD_MENU_LINUX, IDC_RELOAD }, { MENU_SEPARATOR, MENU_SEPARATOR }, { IDS_FULLSCREEN, IDC_FULLSCREEN }, { IDS_TEXT_DEFAULT_LINUX, IDC_ZOOM_NORMAL }, { IDS_TEXT_BIGGER_LINUX, IDC_ZOOM_PLUS }, { IDS_TEXT_SMALLER_LINUX, IDC_ZOOM_MINUS }, { MENU_END, MENU_END } }; GlobalMenuBarCommand history_menu[] = { { IDS_HISTORY_HOME_LINUX, IDC_HOME }, { IDS_HISTORY_BACK_LINUX, IDC_BACK }, { IDS_HISTORY_FORWARD_LINUX, IDC_FORWARD }, { MENU_SEPARATOR, MENU_SEPARATOR }, { IDS_HISTORY_VISITED_LINUX, MENU_DISABLED_ID, TAG_MOST_VISITED_HEADER }, { MENU_SEPARATOR, MENU_SEPARATOR }, { IDS_HISTORY_CLOSED_LINUX, MENU_DISABLED_ID, TAG_RECENTLY_CLOSED_HEADER }, { MENU_SEPARATOR, MENU_SEPARATOR }, { IDS_SHOWFULLHISTORY_LINK, IDC_SHOW_HISTORY }, { MENU_END, MENU_END } }; GlobalMenuBarCommand tools_menu[] = { { IDS_SHOW_DOWNLOADS, IDC_SHOW_DOWNLOADS }, { IDS_SHOW_HISTORY, IDC_SHOW_HISTORY }, { IDS_SHOW_EXTENSIONS, IDC_MANAGE_EXTENSIONS }, { MENU_SEPARATOR, MENU_SEPARATOR }, { IDS_TASK_MANAGER, IDC_TASK_MANAGER }, { IDS_CLEAR_BROWSING_DATA, IDC_CLEAR_BROWSING_DATA }, { MENU_SEPARATOR, MENU_SEPARATOR }, { IDS_VIEW_SOURCE, IDC_VIEW_SOURCE }, { IDS_DEV_TOOLS, IDC_DEV_TOOLS }, { IDS_DEV_TOOLS_CONSOLE, IDC_DEV_TOOLS_CONSOLE }, { IDS_DEV_TOOLS_DEVICES, IDC_DEV_TOOLS_DEVICES }, { MENU_END, MENU_END } }; GlobalMenuBarCommand help_menu[] = { #if defined(GOOGLE_CHROME_BUILD) { IDS_FEEDBACK, IDC_FEEDBACK }, #endif { IDS_HELP_PAGE , IDC_HELP_PAGE_VIA_MENU }, { MENU_END, MENU_END } }; void EnsureMethodsLoaded() { static bool attempted_load = false; if (attempted_load) return; attempted_load = true; void* dbusmenu_lib = dlopen("libdbusmenu-glib.so", RTLD_LAZY); if (!dbusmenu_lib) dbusmenu_lib = dlopen("libdbusmenu-glib.so.4", RTLD_LAZY); if (!dbusmenu_lib) return; // DbusmenuMenuItem methods. menuitem_new = reinterpret_cast( dlsym(dbusmenu_lib, "dbusmenu_menuitem_new")); menuitem_child_add_position = reinterpret_cast( dlsym(dbusmenu_lib, "dbusmenu_menuitem_child_add_position")); menuitem_child_append = reinterpret_cast( dlsym(dbusmenu_lib, "dbusmenu_menuitem_child_append")); menuitem_child_delete = reinterpret_cast( dlsym(dbusmenu_lib, "dbusmenu_menuitem_child_delete")); menuitem_get_children = reinterpret_cast( dlsym(dbusmenu_lib, "dbusmenu_menuitem_get_children")); menuitem_property_set = reinterpret_cast( dlsym(dbusmenu_lib, "dbusmenu_menuitem_property_set")); menuitem_property_set_variant = reinterpret_cast( dlsym(dbusmenu_lib, "dbusmenu_menuitem_property_set_variant")); menuitem_property_set_bool = reinterpret_cast( dlsym(dbusmenu_lib, "dbusmenu_menuitem_property_set_bool")); menuitem_property_set_int = reinterpret_cast( dlsym(dbusmenu_lib, "dbusmenu_menuitem_property_set_int")); // DbusmenuServer methods. server_new = reinterpret_cast( dlsym(dbusmenu_lib, "dbusmenu_server_new")); server_set_root = reinterpret_cast( dlsym(dbusmenu_lib, "dbusmenu_server_set_root")); } } // namespace struct GlobalMenuBarX11::HistoryItem { HistoryItem() : session_id(0) {} // The title for the menu item. base::string16 title; // The URL that will be navigated to if the user selects this item. GURL url; // This ID is unique for a browser session and can be passed to the // TabRestoreService to re-open the closed window or tab that this // references. A non-0 session ID indicates that this is an entry can be // restored that way. Otherwise, the URL will be used to open the item and // this ID will be 0. SessionID::id_type session_id; // If the HistoryItem is a window, this will be the vector of tabs. Note // that this is a list of weak references. The |menu_item_map_| is the owner // of all items. If it is not a window, then the entry is a single page and // the vector will be empty. std::vector tabs; private: DISALLOW_COPY_AND_ASSIGN(HistoryItem); }; GlobalMenuBarX11::GlobalMenuBarX11(BrowserView* browser_view, BrowserDesktopWindowTreeHostX11* host) : browser_(browser_view->browser()), profile_(browser_->profile()), browser_view_(browser_view), host_(host), server_(NULL), root_item_(NULL), history_menu_(NULL), top_sites_(NULL), tab_restore_service_(NULL), weak_ptr_factory_(this) { EnsureMethodsLoaded(); if (server_new) host_->AddObserver(this); } GlobalMenuBarX11::~GlobalMenuBarX11() { if (server_) { Disable(); if (tab_restore_service_) tab_restore_service_->RemoveObserver(this); g_object_unref(server_); host_->RemoveObserver(this); } } // static std::string GlobalMenuBarX11::GetPathForWindow(unsigned long xid) { return base::StringPrintf("/com/canonical/menu/%lX", xid); } DbusmenuMenuitem* GlobalMenuBarX11::BuildSeparator() { DbusmenuMenuitem* item = menuitem_new(); menuitem_property_set(item, kPropertyType, kTypeSeparator); menuitem_property_set_bool(item, kPropertyVisible, true); return item; } DbusmenuMenuitem* GlobalMenuBarX11::BuildMenuItem( const std::string& label, int tag_id) { DbusmenuMenuitem* item = menuitem_new(); menuitem_property_set(item, kPropertyLabel, label.c_str()); menuitem_property_set_bool(item, kPropertyVisible, true); if (tag_id) g_object_set_data(G_OBJECT(item), kTypeTag, GINT_TO_POINTER(tag_id)); return item; } void GlobalMenuBarX11::InitServer(unsigned long xid) { std::string path = GetPathForWindow(xid); { ANNOTATE_SCOPED_MEMORY_LEAK; // http://crbug.com/314087 server_ = server_new(path.c_str()); } root_item_ = menuitem_new(); menuitem_property_set(root_item_, kPropertyLabel, "Root"); menuitem_property_set_bool(root_item_, kPropertyVisible, true); // First build static menu content. BuildStaticMenu(root_item_, IDS_FILE_MENU_LINUX, file_menu); BuildStaticMenu(root_item_, IDS_EDIT_MENU_LINUX, edit_menu); BuildStaticMenu(root_item_, IDS_VIEW_MENU_LINUX, view_menu); history_menu_ = BuildStaticMenu( root_item_, IDS_HISTORY_MENU_LINUX, history_menu); BuildStaticMenu(root_item_, IDS_TOOLS_MENU_LINUX, tools_menu); BuildStaticMenu(root_item_, IDS_HELP_MENU_LINUX, help_menu); // We have to connect to |history_menu_item|'s "activate" signal instead of // |history_menu|'s "show" signal because we are not supposed to modify the // menu during "show" g_signal_connect(history_menu_, "about-to-show", G_CALLBACK(OnHistoryMenuAboutToShowThunk), this); for (CommandIDMenuItemMap::const_iterator it = id_to_menu_item_.begin(); it != id_to_menu_item_.end(); ++it) { menuitem_property_set_bool(it->second, kPropertyEnabled, chrome::IsCommandEnabled(browser_, it->first)); ui::Accelerator accelerator; if (browser_view_->GetAccelerator(it->first, &accelerator)) RegisterAccelerator(it->second, accelerator); chrome::AddCommandObserver(browser_, it->first, this); } pref_change_registrar_.Init(browser_->profile()->GetPrefs()); pref_change_registrar_.Add( prefs::kShowBookmarkBar, base::Bind(&GlobalMenuBarX11::OnBookmarkBarVisibilityChanged, base::Unretained(this))); OnBookmarkBarVisibilityChanged(); top_sites_ = profile_->GetTopSites(); if (top_sites_) { GetTopSitesData(); // Register for notification when TopSites changes so that we can update // ourself. registrar_.Add(this, chrome::NOTIFICATION_TOP_SITES_CHANGED, content::Source(top_sites_)); } server_set_root(server_, root_item_); } void GlobalMenuBarX11::Disable() { for (CommandIDMenuItemMap::const_iterator it = id_to_menu_item_.begin(); it != id_to_menu_item_.end(); ++it) { chrome::RemoveCommandObserver(browser_, it->first, this); } id_to_menu_item_.clear(); pref_change_registrar_.RemoveAll(); } DbusmenuMenuitem* GlobalMenuBarX11::BuildStaticMenu( DbusmenuMenuitem* parent, int menu_str_id, GlobalMenuBarCommand* commands) { DbusmenuMenuitem* top = menuitem_new(); menuitem_property_set( top, kPropertyLabel, ui::RemoveWindowsStyleAccelerators( l10n_util::GetStringUTF8(menu_str_id)).c_str()); menuitem_property_set_bool(top, kPropertyVisible, true); for (int i = 0; commands[i].str_id != MENU_END; ++i) { DbusmenuMenuitem* menu_item = NULL; int command_id = commands[i].command; if (commands[i].str_id == MENU_SEPARATOR) { menu_item = BuildSeparator(); } else { std::string label = ui::ConvertAcceleratorsFromWindowsStyle( l10n_util::GetStringUTF8(commands[i].str_id)); menu_item = BuildMenuItem(label, commands[i].tag); if (command_id == MENU_DISABLED_ID) { menuitem_property_set_bool(menu_item, kPropertyEnabled, false); } else { if (command_id == IDC_SHOW_BOOKMARK_BAR) menuitem_property_set(menu_item, kPropertyToggleType, kTypeCheckmark); id_to_menu_item_.insert(std::make_pair(command_id, menu_item)); g_object_set_data(G_OBJECT(menu_item), "command-id", GINT_TO_POINTER(command_id)); g_signal_connect(menu_item, "item-activated", G_CALLBACK(OnItemActivatedThunk), this); } } menuitem_child_append(top, menu_item); g_object_unref(menu_item); } menuitem_child_append(parent, top); g_object_unref(top); return top; } void GlobalMenuBarX11::RegisterAccelerator(DbusmenuMenuitem* item, const ui::Accelerator& accelerator) { // A translation of libdbusmenu-gtk's menuitem_property_set_shortcut() // translated from GDK types to ui::Accelerator types. GVariantBuilder builder; g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); if (accelerator.IsCtrlDown()) g_variant_builder_add(&builder, "s", "Control"); if (accelerator.IsAltDown()) g_variant_builder_add(&builder, "s", "Alt"); if (accelerator.IsShiftDown()) g_variant_builder_add(&builder, "s", "Shift"); char* name = XKeysymToString(XKeysymForWindowsKeyCode( accelerator.key_code(), false)); if (!name) { NOTIMPLEMENTED(); return; } g_variant_builder_add(&builder, "s", name); GVariant* inside_array = g_variant_builder_end(&builder); g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); g_variant_builder_add_value(&builder, inside_array); GVariant* outside_array = g_variant_builder_end(&builder); menuitem_property_set_variant(item, kPropertyShortcut, outside_array); } GlobalMenuBarX11::HistoryItem* GlobalMenuBarX11::HistoryItemForTab( const TabRestoreService::Tab& entry) { const sessions::SerializedNavigationEntry& current_navigation = entry.navigations.at(entry.current_navigation_index); HistoryItem* item = new HistoryItem(); item->title = current_navigation.title(); item->url = current_navigation.virtual_url(); item->session_id = entry.id; return item; } void GlobalMenuBarX11::AddHistoryItemToMenu(HistoryItem* item, DbusmenuMenuitem* menu, int tag, int index) { base::string16 title = item->title; std::string url_string = item->url.possibly_invalid_spec(); if (title.empty()) title = base::UTF8ToUTF16(url_string); gfx::ElideString(title, kMaximumMenuWidthInChars, &title); DbusmenuMenuitem* menu_item = BuildMenuItem(base::UTF16ToUTF8(title), tag); g_signal_connect(menu_item, "item-activated", G_CALLBACK(OnHistoryItemActivatedThunk), this); g_object_set_data_full(G_OBJECT(menu_item), kHistoryItem, item, DeleteHistoryItem); menuitem_child_add_position(menu, menu_item, index); g_object_unref(menu_item); } void GlobalMenuBarX11::GetTopSitesData() { DCHECK(top_sites_); top_sites_->GetMostVisitedURLs( base::Bind(&GlobalMenuBarX11::OnTopSitesReceived, weak_ptr_factory_.GetWeakPtr()), false); } void GlobalMenuBarX11::OnTopSitesReceived( const history::MostVisitedURLList& visited_list) { ClearMenuSection(history_menu_, TAG_MOST_VISITED); int index = GetIndexOfMenuItemWithTag(history_menu_, TAG_MOST_VISITED_HEADER) + 1; for (size_t i = 0; i < visited_list.size() && i < kMostVisitedCount; ++i) { const history::MostVisitedURL& visited = visited_list[i]; if (visited.url.spec().empty()) break; // This is the signal that there are no more real visited sites. HistoryItem* item = new HistoryItem(); item->title = visited.title; item->url = visited.url; AddHistoryItemToMenu(item, history_menu_, TAG_MOST_VISITED, index++); } } void GlobalMenuBarX11::OnBookmarkBarVisibilityChanged() { CommandIDMenuItemMap::iterator it = id_to_menu_item_.find(IDC_SHOW_BOOKMARK_BAR); if (it != id_to_menu_item_.end()) { PrefService* prefs = browser_->profile()->GetPrefs(); // Note: Unlike the GTK version, we don't appear to need to do tricks where // we block activation while setting the toggle. menuitem_property_set_int(it->second, kPropertyToggleState, prefs->GetBoolean(prefs::kShowBookmarkBar)); } } int GlobalMenuBarX11::GetIndexOfMenuItemWithTag(DbusmenuMenuitem* menu, int tag_id) { GList* childs = menuitem_get_children(menu); int i = 0; for (; childs != NULL; childs = childs->next, i++) { int tag = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(childs->data), kTypeTag)); if (tag == tag_id) return i; } NOTREACHED(); return -1; } void GlobalMenuBarX11::ClearMenuSection(DbusmenuMenuitem* menu, int tag_id) { std::vector menuitems_to_delete; GList* childs = menuitem_get_children(menu); for (; childs != NULL; childs = childs->next) { DbusmenuMenuitem* current_item = reinterpret_cast( childs->data); ClearMenuSection(current_item, tag_id); int tag = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(childs->data), kTypeTag)); if (tag == tag_id) menuitems_to_delete.push_back(current_item); } for (std::vector::const_iterator it = menuitems_to_delete.begin(); it != menuitems_to_delete.end(); ++it) { menuitem_child_delete(menu, *it); } } // static void GlobalMenuBarX11::DeleteHistoryItem(void* void_item) { HistoryItem* item = reinterpret_cast(void_item); delete item; } void GlobalMenuBarX11::EnabledStateChangedForCommand(int id, bool enabled) { CommandIDMenuItemMap::iterator it = id_to_menu_item_.find(id); if (it != id_to_menu_item_.end()) menuitem_property_set_bool(it->second, kPropertyEnabled, enabled); } void GlobalMenuBarX11::Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) { if (type == chrome::NOTIFICATION_TOP_SITES_CHANGED) { GetTopSitesData(); } else { NOTREACHED(); } } void GlobalMenuBarX11::TabRestoreServiceChanged(TabRestoreService* service) { const TabRestoreService::Entries& entries = service->entries(); ClearMenuSection(history_menu_, TAG_RECENTLY_CLOSED); // We'll get the index the "Recently Closed" header. (This can vary depending // on the number of "Most Visited" items. int index = GetIndexOfMenuItemWithTag(history_menu_, TAG_RECENTLY_CLOSED_HEADER) + 1; unsigned int added_count = 0; for (TabRestoreService::Entries::const_iterator it = entries.begin(); it != entries.end() && added_count < kRecentlyClosedCount; ++it) { TabRestoreService::Entry* entry = *it; if (entry->type == TabRestoreService::WINDOW) { TabRestoreService::Window* entry_win = static_cast(entry); std::vector& tabs = entry_win->tabs; if (tabs.empty()) continue; // Create the item for the parent/window. HistoryItem* item = new HistoryItem(); item->session_id = entry_win->id; std::string title = item->tabs.size() == 1 ? l10n_util::GetStringUTF8( IDS_NEW_TAB_RECENTLY_CLOSED_WINDOW_SINGLE) : l10n_util::GetStringFUTF8( IDS_NEW_TAB_RECENTLY_CLOSED_WINDOW_MULTIPLE, base::IntToString16(item->tabs.size())); DbusmenuMenuitem* parent_item = BuildMenuItem( title, TAG_RECENTLY_CLOSED); menuitem_child_add_position(history_menu_, parent_item, index++); g_object_unref(parent_item); // The mac version of this code allows the user to click on the parent // menu item to have the same effect as clicking the restore window // submenu item. GTK+ helpfully activates a menu item when it shows a // submenu so toss that feature out. DbusmenuMenuitem* restore_item = BuildMenuItem( l10n_util::GetStringUTF8( IDS_HISTORY_CLOSED_RESTORE_WINDOW_LINUX).c_str(), TAG_RECENTLY_CLOSED); g_signal_connect(restore_item, "item-activated", G_CALLBACK(OnHistoryItemActivatedThunk), this); g_object_set_data_full(G_OBJECT(restore_item), kHistoryItem, item, DeleteHistoryItem); menuitem_child_append(parent_item, restore_item); g_object_unref(restore_item); DbusmenuMenuitem* separator = BuildSeparator(); menuitem_child_append(parent_item, separator); g_object_unref(separator); // Loop over the window's tabs and add them to the submenu. int subindex = 2; std::vector::const_iterator iter; for (iter = tabs.begin(); iter != tabs.end(); ++iter) { TabRestoreService::Tab tab = *iter; HistoryItem* tab_item = HistoryItemForTab(tab); item->tabs.push_back(tab_item); AddHistoryItemToMenu(tab_item, parent_item, TAG_RECENTLY_CLOSED, subindex++); } ++added_count; } else if (entry->type == TabRestoreService::TAB) { TabRestoreService::Tab* tab = static_cast(entry); HistoryItem* item = HistoryItemForTab(*tab); AddHistoryItemToMenu(item, history_menu_, TAG_RECENTLY_CLOSED, index++); ++added_count; } } } void GlobalMenuBarX11::TabRestoreServiceDestroyed( TabRestoreService* service) { tab_restore_service_ = NULL; } void GlobalMenuBarX11::OnWindowMapped(unsigned long xid) { if (!server_) InitServer(xid); GlobalMenuBarRegistrarX11::GetInstance()->OnWindowMapped(xid); } void GlobalMenuBarX11::OnWindowUnmapped(unsigned long xid) { GlobalMenuBarRegistrarX11::GetInstance()->OnWindowUnmapped(xid); } void GlobalMenuBarX11::OnItemActivated(DbusmenuMenuitem* item, unsigned int timestamp) { int id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item), "command-id")); chrome::ExecuteCommand(browser_, id); } void GlobalMenuBarX11::OnHistoryItemActivated(DbusmenuMenuitem* sender, unsigned int timestamp) { // Note: We don't have access to the event modifiers used to click the menu // item since that happens in a different process. HistoryItem* item = reinterpret_cast( g_object_get_data(G_OBJECT(sender), kHistoryItem)); // If this item can be restored using TabRestoreService, do so. Otherwise, // just load the URL. TabRestoreService* service = TabRestoreServiceFactory::GetForProfile(profile_); if (item->session_id && service) { service->RestoreEntryById(browser_->tab_restore_service_delegate(), item->session_id, browser_->host_desktop_type(), UNKNOWN); } else { DCHECK(item->url.is_valid()); browser_->OpenURL(content::OpenURLParams( item->url, content::Referrer(), NEW_FOREGROUND_TAB, content::PAGE_TRANSITION_AUTO_BOOKMARK, false)); } } void GlobalMenuBarX11::OnHistoryMenuAboutToShow(DbusmenuMenuitem* item) { if (!tab_restore_service_) { tab_restore_service_ = TabRestoreServiceFactory::GetForProfile(profile_); if (tab_restore_service_) { tab_restore_service_->LoadTabsFromLastSession(); tab_restore_service_->AddObserver(this); // If LoadTabsFromLastSession doesn't load tabs, it won't call // TabRestoreServiceChanged(). This ensures that all new windows after // the first one will have their menus populated correctly. TabRestoreServiceChanged(tab_restore_service_); } } }