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