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