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