[Eraser strings] Remove unused Supervised User infobar and corresponding strings
[chromium-blink-merge.git] / chrome / browser / ui / toolbar / recent_tabs_sub_menu_model.cc
blob45163e0f2528b0f7e60e7cffd4829127009369fb
1 // Copyright 2012 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/toolbar/recent_tabs_sub_menu_model.h"
7 #include "base/bind.h"
8 #include "base/metrics/histogram.h"
9 #include "base/prefs/scoped_user_pref_update.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "chrome/app/chrome_command_ids.h"
13 #include "chrome/browser/favicon/favicon_service_factory.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/search/search.h"
16 #include "chrome/browser/sessions/session_restore.h"
17 #include "chrome/browser/sessions/tab_restore_service.h"
18 #include "chrome/browser/sessions/tab_restore_service_delegate.h"
19 #include "chrome/browser/sessions/tab_restore_service_factory.h"
20 #include "chrome/browser/sync/profile_sync_service.h"
21 #include "chrome/browser/sync/profile_sync_service_factory.h"
22 #include "chrome/browser/ui/browser.h"
23 #include "chrome/browser/ui/browser_commands.h"
24 #include "chrome/browser/ui/tabs/tab_strip_model.h"
25 #include "chrome/browser/ui/toolbar/wrench_menu_model.h"
26 #include "chrome/grit/generated_resources.h"
27 #include "components/favicon_base/favicon_types.h"
28 #include "components/sync_driver/glue/synced_session.h"
29 #include "components/sync_driver/open_tabs_ui_delegate.h"
30 #include "content/public/browser/user_metrics.h"
31 #include "grit/browser_resources.h"
32 #include "grit/theme_resources.h"
33 #include "ui/base/accelerators/accelerator.h"
34 #include "ui/base/l10n/l10n_util.h"
35 #include "ui/base/resource/resource_bundle.h"
36 #include "ui/resources/grit/ui_resources.h"
38 #if defined(USE_ASH)
39 #include "ash/accelerators/accelerator_table.h"
40 #endif // defined(USE_ASH)
42 namespace {
44 // Initial comamnd ID's for navigatable (and hence executable) tab/window menu
45 // items. The menumodel and storage structures are not 1-1:
46 // - menumodel has "Recently closed" header, "No tabs from other devices",
47 // device section headers, separators, local and other devices' tab items, and
48 // local window items.
49 // - |local_tab_navigation_items_| and |other_devices_tab_navigation_items_|
50 // only have navigatabale/executable tab items.
51 // - |local_window_items_| only has executable open window items.
52 // Using initial command IDs for local tab, local window and other devices' tab
53 // items makes it easier and less error-prone to manipulate the menumodel and
54 // storage structures. These ids must be bigger than the maximum possible
55 // number of items in the menumodel, so that index of the last menu item doesn't
56 // clash with these values when menu items are retrieved via
57 // GetIndexOfCommandId().
58 // The range of all command ID's used in RecentTabsSubMenuModel, including the
59 // "Recently closed" headers, must be between
60 // |WrenchMenuModel::kMinRecentTabsCommandId| i.e. 1001 and 1200
61 // (|WrenchMenuModel::kMaxRecentTabsCommandId|) inclusively.
62 const int kFirstLocalTabCommandId = WrenchMenuModel::kMinRecentTabsCommandId;
63 const int kFirstLocalWindowCommandId = 1031;
64 const int kFirstOtherDevicesTabCommandId = 1051;
65 const int kMinDeviceNameCommandId = 1100;
66 const int kMaxDeviceNameCommandId = 1110;
68 // The maximum number of local recently closed entries (tab or window) to be
69 // shown in the menu.
70 const int kMaxLocalEntries = 8;
72 // Index of the separator that follows the history menu item. Used as a
73 // reference position for inserting local entries.
74 const int kHistorySeparatorIndex = 1;
76 // Comparator function for use with std::sort that will sort sessions by
77 // descending modified_time (i.e., most recent first).
78 bool SortSessionsByRecency(const sync_driver::SyncedSession* s1,
79 const sync_driver::SyncedSession* s2) {
80 return s1->modified_time > s2->modified_time;
83 // Returns true if the command id identifies a tab menu item.
84 bool IsTabModelCommandId(int command_id) {
85 return ((command_id >= kFirstLocalTabCommandId &&
86 command_id < kFirstLocalWindowCommandId) ||
87 (command_id >= kFirstOtherDevicesTabCommandId &&
88 command_id < kMinDeviceNameCommandId));
91 // Returns true if the command id identifies a window menu item.
92 bool IsWindowModelCommandId(int command_id) {
93 return command_id >= kFirstLocalWindowCommandId &&
94 command_id < kFirstOtherDevicesTabCommandId;
97 bool IsDeviceNameCommandId(int command_id) {
98 return command_id >= kMinDeviceNameCommandId &&
99 command_id <= kMaxDeviceNameCommandId;
102 // Convert |tab_vector_index| to command id of menu item, with
103 // |first_command_id| as the base command id.
104 int TabVectorIndexToCommandId(int tab_vector_index, int first_command_id) {
105 int command_id = tab_vector_index + first_command_id;
106 DCHECK(IsTabModelCommandId(command_id));
107 return command_id;
110 // Convert |window_vector_index| to command id of menu item.
111 int WindowVectorIndexToCommandId(int window_vector_index) {
112 int command_id = window_vector_index + kFirstLocalWindowCommandId;
113 DCHECK(IsWindowModelCommandId(command_id));
114 return command_id;
117 // Convert |command_id| of menu item to index in |local_window_items_|.
118 int CommandIdToWindowVectorIndex(int command_id) {
119 DCHECK(IsWindowModelCommandId(command_id));
120 return command_id - kFirstLocalWindowCommandId;
123 } // namespace
125 enum RecentTabAction {
126 LOCAL_SESSION_TAB = 0,
127 OTHER_DEVICE_TAB,
128 RESTORE_WINDOW,
129 SHOW_MORE,
130 LIMIT_RECENT_TAB_ACTION
133 // An element in |RecentTabsSubMenuModel::local_tab_navigation_items_| or
134 // |RecentTabsSubMenuModel::other_devices_tab_navigation_items_| that stores
135 // the navigation information of a local or other devices' tab required to
136 // restore the tab.
137 struct RecentTabsSubMenuModel::TabNavigationItem {
138 TabNavigationItem() : tab_id(-1) {}
140 TabNavigationItem(const std::string& session_tag,
141 const SessionID::id_type& tab_id,
142 const base::string16& title,
143 const GURL& url)
144 : session_tag(session_tag),
145 tab_id(tab_id),
146 title(title),
147 url(url) {}
149 // For use by std::set for sorting.
150 bool operator<(const TabNavigationItem& other) const {
151 return url < other.url;
154 // Empty for local tabs, non-empty for other devices' tabs.
155 std::string session_tag;
156 SessionID::id_type tab_id; // -1 for invalid, >= 0 otherwise.
157 base::string16 title;
158 GURL url;
161 const int RecentTabsSubMenuModel::kRecentlyClosedHeaderCommandId = 1120;
162 const int RecentTabsSubMenuModel::kDisabledRecentlyClosedHeaderCommandId = 1121;
164 RecentTabsSubMenuModel::RecentTabsSubMenuModel(
165 ui::AcceleratorProvider* accelerator_provider,
166 Browser* browser,
167 sync_driver::OpenTabsUIDelegate* open_tabs_delegate)
168 : ui::SimpleMenuModel(this),
169 browser_(browser),
170 open_tabs_delegate_(open_tabs_delegate),
171 last_local_model_index_(kHistorySeparatorIndex),
172 default_favicon_(ui::ResourceBundle::GetSharedInstance().
173 GetNativeImageNamed(IDR_DEFAULT_FAVICON)),
174 weak_ptr_factory_(this) {
175 // Invoke asynchronous call to load tabs from local last session, which does
176 // nothing if the tabs have already been loaded or they shouldn't be loaded.
177 // TabRestoreServiceChanged() will be called after the tabs are loaded.
178 TabRestoreService* service =
179 TabRestoreServiceFactory::GetForProfile(browser_->profile());
180 if (service) {
181 service->LoadTabsFromLastSession();
183 // TODO(sail): enable this when mac implements the dynamic menu, together with
184 // MenuModelDelegate::MenuStructureChanged().
185 #if !defined(OS_MACOSX)
186 service->AddObserver(this);
187 #endif
190 Build();
192 // Retrieve accelerator key for IDC_RESTORE_TAB now, because on ASH, it's not
193 // defined in |accelerator_provider|, but in shell, so simply retrieve it now
194 // for all ASH and non-ASH for use in |GetAcceleratorForCommandId|.
195 #if defined(USE_ASH)
196 for (size_t i = 0; i < ash::kAcceleratorDataLength; ++i) {
197 const ash::AcceleratorData& accel_data = ash::kAcceleratorData[i];
198 if (accel_data.action == ash::RESTORE_TAB) {
199 reopen_closed_tab_accelerator_ = ui::Accelerator(accel_data.keycode,
200 accel_data.modifiers);
201 break;
204 #else
205 if (accelerator_provider) {
206 accelerator_provider->GetAcceleratorForCommandId(
207 IDC_RESTORE_TAB, &reopen_closed_tab_accelerator_);
209 #endif // defined(USE_ASH)
211 if (accelerator_provider) {
212 accelerator_provider->GetAcceleratorForCommandId(
213 IDC_SHOW_HISTORY, &show_history_accelerator_);
217 RecentTabsSubMenuModel::~RecentTabsSubMenuModel() {
218 TabRestoreService* service =
219 TabRestoreServiceFactory::GetForProfile(browser_->profile());
220 if (service)
221 service->RemoveObserver(this);
224 bool RecentTabsSubMenuModel::IsCommandIdChecked(int command_id) const {
225 return false;
228 bool RecentTabsSubMenuModel::IsCommandIdEnabled(int command_id) const {
229 if (command_id == kRecentlyClosedHeaderCommandId ||
230 command_id == kDisabledRecentlyClosedHeaderCommandId ||
231 command_id == IDC_RECENT_TABS_NO_DEVICE_TABS ||
232 IsDeviceNameCommandId(command_id)) {
233 return false;
235 return true;
238 bool RecentTabsSubMenuModel::GetAcceleratorForCommandId(
239 int command_id, ui::Accelerator* accelerator) {
240 // If there are no recently closed items, we show the accelerator beside
241 // the header, otherwise, we show it beside the first item underneath it.
242 int index_in_menu = GetIndexOfCommandId(command_id);
243 int header_index = GetIndexOfCommandId(kRecentlyClosedHeaderCommandId);
244 if ((command_id == kDisabledRecentlyClosedHeaderCommandId ||
245 (header_index != -1 && index_in_menu == header_index + 1)) &&
246 reopen_closed_tab_accelerator_.key_code() != ui::VKEY_UNKNOWN) {
247 *accelerator = reopen_closed_tab_accelerator_;
248 return true;
251 if (command_id == IDC_SHOW_HISTORY) {
252 *accelerator = show_history_accelerator_;
253 return true;
256 return false;
259 void RecentTabsSubMenuModel::ExecuteCommand(int command_id, int event_flags) {
260 if (command_id == IDC_SHOW_HISTORY) {
261 UMA_HISTOGRAM_ENUMERATION("WrenchMenu.RecentTabsSubMenu", SHOW_MORE,
262 LIMIT_RECENT_TAB_ACTION);
263 // We show all "other devices" on the history page.
264 chrome::ExecuteCommandWithDisposition(browser_, IDC_SHOW_HISTORY,
265 ui::DispositionFromEventFlags(event_flags));
266 return;
269 DCHECK_NE(IDC_RECENT_TABS_NO_DEVICE_TABS, command_id);
270 DCHECK(!IsDeviceNameCommandId(command_id));
272 WindowOpenDisposition disposition =
273 ui::DispositionFromEventFlags(event_flags);
274 if (disposition == CURRENT_TAB) // Force to open a new foreground tab.
275 disposition = NEW_FOREGROUND_TAB;
277 TabRestoreService* service =
278 TabRestoreServiceFactory::GetForProfile(browser_->profile());
279 TabRestoreServiceDelegate* delegate =
280 TabRestoreServiceDelegate::FindDelegateForWebContents(
281 browser_->tab_strip_model()->GetActiveWebContents());
282 if (IsTabModelCommandId(command_id)) {
283 TabNavigationItems* tab_items = NULL;
284 int tab_items_idx = CommandIdToTabVectorIndex(command_id, &tab_items);
285 const TabNavigationItem& item = (*tab_items)[tab_items_idx];
286 DCHECK(item.tab_id > -1 && item.url.is_valid());
288 if (item.session_tag.empty()) { // Restore tab of local session.
289 if (service && delegate) {
290 content::RecordAction(
291 base::UserMetricsAction("WrenchMenu_OpenRecentTabFromLocal"));
292 UMA_HISTOGRAM_ENUMERATION("WrenchMenu.RecentTabsSubMenu",
293 LOCAL_SESSION_TAB, LIMIT_RECENT_TAB_ACTION);
294 service->RestoreEntryById(delegate, item.tab_id,
295 browser_->host_desktop_type(), disposition);
297 } else { // Restore tab of session from other devices.
298 sync_driver::OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate();
299 if (!open_tabs)
300 return;
301 const sessions::SessionTab* tab;
302 if (!open_tabs->GetForeignTab(item.session_tag, item.tab_id, &tab))
303 return;
304 if (tab->navigations.empty())
305 return;
306 content::RecordAction(
307 base::UserMetricsAction("WrenchMenu_OpenRecentTabFromDevice"));
308 UMA_HISTOGRAM_ENUMERATION("WrenchMenu.RecentTabsSubMenu",
309 OTHER_DEVICE_TAB, LIMIT_RECENT_TAB_ACTION);
310 SessionRestore::RestoreForeignSessionTab(
311 browser_->tab_strip_model()->GetActiveWebContents(),
312 *tab, disposition);
314 } else {
315 DCHECK(IsWindowModelCommandId(command_id));
316 if (service && delegate) {
317 int window_items_idx = CommandIdToWindowVectorIndex(command_id);
318 DCHECK(window_items_idx >= 0 &&
319 window_items_idx < static_cast<int>(local_window_items_.size()));
320 content::RecordAction(
321 base::UserMetricsAction("WrenchMenu_OpenRecentWindow"));
322 UMA_HISTOGRAM_ENUMERATION("WrenchMenu.RecentTabsSubMenu", RESTORE_WINDOW,
323 LIMIT_RECENT_TAB_ACTION);
324 service->RestoreEntryById(delegate, local_window_items_[window_items_idx],
325 browser_->host_desktop_type(), disposition);
328 UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.OpenRecentTab",
329 menu_opened_timer_.Elapsed());
330 UMA_HISTOGRAM_ENUMERATION("WrenchMenu.MenuAction", MENU_ACTION_RECENT_TAB,
331 LIMIT_MENU_ACTION);
334 int RecentTabsSubMenuModel::GetFirstRecentTabsCommandId() {
335 return WindowVectorIndexToCommandId(0);
338 const gfx::FontList* RecentTabsSubMenuModel::GetLabelFontListAt(
339 int index) const {
340 int command_id = GetCommandIdAt(index);
341 if (command_id == kRecentlyClosedHeaderCommandId ||
342 IsDeviceNameCommandId(command_id)) {
343 return &ui::ResourceBundle::GetSharedInstance().GetFontList(
344 ui::ResourceBundle::BoldFont);
346 return NULL;
349 int RecentTabsSubMenuModel::GetMaxWidthForItemAtIndex(int item_index) const {
350 int command_id = GetCommandIdAt(item_index);
351 if (command_id == IDC_RECENT_TABS_NO_DEVICE_TABS ||
352 command_id == kRecentlyClosedHeaderCommandId ||
353 command_id == kDisabledRecentlyClosedHeaderCommandId) {
354 return -1;
356 return 320;
359 bool RecentTabsSubMenuModel::GetURLAndTitleForItemAtIndex(
360 int index,
361 std::string* url,
362 base::string16* title) {
363 int command_id = GetCommandIdAt(index);
364 if (IsTabModelCommandId(command_id)) {
365 TabNavigationItems* tab_items = NULL;
366 int tab_items_idx = CommandIdToTabVectorIndex(command_id, &tab_items);
367 const TabNavigationItem& item = (*tab_items)[tab_items_idx];
368 *url = item.url.possibly_invalid_spec();
369 *title = item.title;
370 return true;
372 return false;
375 void RecentTabsSubMenuModel::Build() {
376 // The menu contains:
377 // - History to open the full history tab.
378 // - Separator
379 // - Recently closed header, then list of local recently closed tabs/windows,
380 // then separator
381 // - device 1 section header, then list of tabs from device, then separator
382 // - device 2 section header, then list of tabs from device, then separator
383 // - device 3 section header, then list of tabs from device, then separator
384 // |local_tab_navigation_items_| and |other_devices_tab_navigation_items_|
385 // only contain navigatable (and hence executable) tab items for local
386 // recently closed tabs and tabs from other devices respectively.
387 // |local_window_items_| contains the local recently closed windows.
388 InsertItemWithStringIdAt(0, IDC_SHOW_HISTORY, IDS_SHOW_HISTORY);
389 InsertSeparatorAt(1, ui::NORMAL_SEPARATOR);
390 BuildLocalEntries();
391 BuildTabsFromOtherDevices();
394 void RecentTabsSubMenuModel::BuildLocalEntries() {
395 last_local_model_index_ = kHistorySeparatorIndex;
397 // All local items use InsertItem*At() to append or insert a menu item.
398 // We're appending if building the entries for the first time i.e. invoked
399 // from Constructor(), inserting when local entries change subsequently i.e.
400 // invoked from TabRestoreServiceChanged().
401 TabRestoreService* service =
402 TabRestoreServiceFactory::GetForProfile(browser_->profile());
404 if (!service || service->entries().size() == 0) {
405 // This is to show a disabled restore tab entry with the accelerator to
406 // teach users about this command.
407 InsertItemWithStringIdAt(++last_local_model_index_,
408 kDisabledRecentlyClosedHeaderCommandId,
409 IDS_RECENTLY_CLOSED);
410 } else {
411 InsertItemWithStringIdAt(++last_local_model_index_,
412 kRecentlyClosedHeaderCommandId,
413 IDS_RECENTLY_CLOSED);
414 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
415 SetIcon(last_local_model_index_,
416 rb.GetNativeImageNamed(IDR_RECENTLY_CLOSED_WINDOW));
418 int added_count = 0;
419 TabRestoreService::Entries entries = service->entries();
420 for (TabRestoreService::Entries::const_iterator it = entries.begin();
421 it != entries.end() && added_count < kMaxLocalEntries; ++it) {
422 TabRestoreService::Entry* entry = *it;
423 if (entry->type == TabRestoreService::TAB) {
424 TabRestoreService::Tab* tab =
425 static_cast<TabRestoreService::Tab*>(entry);
426 const sessions::SerializedNavigationEntry& current_navigation =
427 tab->navigations.at(tab->current_navigation_index);
428 BuildLocalTabItem(
429 entry->id,
430 current_navigation.title(),
431 current_navigation.virtual_url(),
432 ++last_local_model_index_);
433 } else {
434 DCHECK_EQ(entry->type, TabRestoreService::WINDOW);
435 BuildLocalWindowItem(
436 entry->id,
437 static_cast<TabRestoreService::Window*>(entry)->tabs.size(),
438 ++last_local_model_index_);
440 ++added_count;
443 DCHECK_GE(last_local_model_index_, 0);
446 void RecentTabsSubMenuModel::BuildTabsFromOtherDevices() {
447 // All other devices' items (device headers or tabs) use AddItem*() to append
448 // a menu item, because they are always only built once (i.e. invoked from
449 // Constructor()) and don't change after that.
451 sync_driver::OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate();
452 std::vector<const sync_driver::SyncedSession*> sessions;
453 if (!open_tabs || !open_tabs->GetAllForeignSessions(&sessions)) {
454 AddSeparator(ui::NORMAL_SEPARATOR);
455 AddItemWithStringId(IDC_RECENT_TABS_NO_DEVICE_TABS,
456 IDS_RECENT_TABS_NO_DEVICE_TABS);
457 return;
460 // Sort sessions from most recent to least recent.
461 std::sort(sessions.begin(), sessions.end(), SortSessionsByRecency);
463 const size_t kMaxSessionsToShow = 3;
464 size_t num_sessions_added = 0;
465 for (size_t i = 0;
466 i < sessions.size() && num_sessions_added < kMaxSessionsToShow; ++i) {
467 const sync_driver::SyncedSession* session = sessions[i];
468 const std::string& session_tag = session->session_tag;
470 // Collect tabs from all windows of the session, ordered by recency.
471 std::vector<const sessions::SessionTab*> tabs_in_session;
472 if (!open_tabs->GetForeignSessionTabs(session_tag, &tabs_in_session) ||
473 tabs_in_session.empty())
474 continue;
476 // Add the header for the device session.
477 DCHECK(!session->session_name.empty());
478 AddSeparator(ui::NORMAL_SEPARATOR);
479 int command_id = kMinDeviceNameCommandId + i;
480 DCHECK_LE(command_id, kMaxDeviceNameCommandId);
481 AddItem(command_id, base::UTF8ToUTF16(session->session_name));
482 AddDeviceFavicon(GetItemCount() - 1, session->device_type);
484 // Build tab menu items from sorted session tabs.
485 const size_t kMaxTabsPerSessionToShow = 4;
486 for (size_t k = 0;
487 k < std::min(tabs_in_session.size(), kMaxTabsPerSessionToShow);
488 ++k) {
489 BuildOtherDevicesTabItem(session_tag, *tabs_in_session[k]);
490 } // for all tabs in one session
492 ++num_sessions_added;
493 } // for all sessions
495 // We are not supposed to get here unless at least some items were added.
496 DCHECK_GT(GetItemCount(), 0);
499 void RecentTabsSubMenuModel::BuildLocalTabItem(int session_id,
500 const base::string16& title,
501 const GURL& url,
502 int curr_model_index) {
503 TabNavigationItem item(std::string(), session_id, title, url);
504 int command_id = TabVectorIndexToCommandId(
505 local_tab_navigation_items_.size(), kFirstLocalTabCommandId);
506 // See comments in BuildLocalEntries() about usage of InsertItem*At().
507 // There may be no tab title, in which case, use the url as tab title.
508 InsertItemAt(curr_model_index, command_id,
509 title.empty() ? base::UTF8ToUTF16(item.url.spec()) : title);
510 AddTabFavicon(command_id, item.url);
511 local_tab_navigation_items_.push_back(item);
514 void RecentTabsSubMenuModel::BuildLocalWindowItem(
515 const SessionID::id_type& window_id,
516 int num_tabs,
517 int curr_model_index) {
518 int command_id = WindowVectorIndexToCommandId(local_window_items_.size());
519 // See comments in BuildLocalEntries() about usage of InsertItem*At().
520 InsertItemAt(curr_model_index, command_id, l10n_util::GetPluralStringFUTF16(
521 IDS_RECENTLY_CLOSED_WINDOW, num_tabs));
522 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
523 SetIcon(curr_model_index, rb.GetNativeImageNamed(IDR_RECENTLY_CLOSED_WINDOW));
524 local_window_items_.push_back(window_id);
527 void RecentTabsSubMenuModel::BuildOtherDevicesTabItem(
528 const std::string& session_tag,
529 const sessions::SessionTab& tab) {
530 const sessions::SerializedNavigationEntry& current_navigation =
531 tab.navigations.at(tab.normalized_navigation_index());
532 TabNavigationItem item(session_tag, tab.tab_id.id(),
533 current_navigation.title(),
534 current_navigation.virtual_url());
535 int command_id = TabVectorIndexToCommandId(
536 other_devices_tab_navigation_items_.size(),
537 kFirstOtherDevicesTabCommandId);
538 // See comments in BuildTabsFromOtherDevices() about usage of AddItem*().
539 // There may be no tab title, in which case, use the url as tab title.
540 AddItem(command_id,
541 current_navigation.title().empty() ?
542 base::UTF8ToUTF16(item.url.spec()) : current_navigation.title());
543 AddTabFavicon(command_id, item.url);
544 other_devices_tab_navigation_items_.push_back(item);
547 void RecentTabsSubMenuModel::AddDeviceFavicon(
548 int index_in_menu,
549 sync_driver::SyncedSession::DeviceType device_type) {
550 int favicon_id = -1;
551 switch (device_type) {
552 case sync_driver::SyncedSession::TYPE_PHONE:
553 favicon_id = IDR_PHONE_FAVICON;
554 break;
556 case sync_driver::SyncedSession::TYPE_TABLET:
557 favicon_id = IDR_TABLET_FAVICON;
558 break;
560 case sync_driver::SyncedSession::TYPE_CHROMEOS:
561 case sync_driver::SyncedSession::TYPE_WIN:
562 case sync_driver::SyncedSession::TYPE_MACOSX:
563 case sync_driver::SyncedSession::TYPE_LINUX:
564 case sync_driver::SyncedSession::TYPE_OTHER:
565 case sync_driver::SyncedSession::TYPE_UNSET:
566 favicon_id = IDR_LAPTOP_FAVICON;
567 break;
570 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
571 SetIcon(index_in_menu, rb.GetNativeImageNamed(favicon_id));
574 void RecentTabsSubMenuModel::AddTabFavicon(int command_id, const GURL& url) {
575 bool is_local_tab = command_id < kFirstOtherDevicesTabCommandId;
576 int index_in_menu = GetIndexOfCommandId(command_id);
578 if (!is_local_tab) {
579 // If tab has synced favicon, use it.
580 // Note that currently, other devices' tabs only have favicons if
581 // --sync-tab-favicons switch is on; according to zea@, this flag is now
582 // automatically enabled for iOS and android, and they're looking into
583 // enabling it for other platforms.
584 sync_driver::OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate();
585 scoped_refptr<base::RefCountedMemory> favicon_png;
586 if (open_tabs &&
587 open_tabs->GetSyncedFaviconForPageURL(url.spec(), &favicon_png)) {
588 gfx::Image image = gfx::Image::CreateFrom1xPNGBytes(favicon_png);
589 SetIcon(index_in_menu, image);
590 return;
594 // Otherwise, start to fetch the favicon from local history asynchronously.
595 // Set default icon first.
596 SetIcon(index_in_menu, default_favicon_);
597 // Start request to fetch actual icon if possible.
598 favicon::FaviconService* favicon_service =
599 FaviconServiceFactory::GetForProfile(browser_->profile(),
600 ServiceAccessType::EXPLICIT_ACCESS);
601 if (!favicon_service)
602 return;
604 favicon_service->GetFaviconImageForPageURL(
605 url,
606 base::Bind(&RecentTabsSubMenuModel::OnFaviconDataAvailable,
607 weak_ptr_factory_.GetWeakPtr(),
608 command_id),
609 is_local_tab ? &local_tab_cancelable_task_tracker_
610 : &other_devices_tab_cancelable_task_tracker_);
613 void RecentTabsSubMenuModel::OnFaviconDataAvailable(
614 int command_id,
615 const favicon_base::FaviconImageResult& image_result) {
616 if (image_result.image.IsEmpty())
617 return;
618 int index_in_menu = GetIndexOfCommandId(command_id);
619 DCHECK_GT(index_in_menu, -1);
620 SetIcon(index_in_menu, image_result.image);
621 ui::MenuModelDelegate* menu_model_delegate = GetMenuModelDelegate();
622 if (menu_model_delegate)
623 menu_model_delegate->OnIconChanged(index_in_menu);
626 int RecentTabsSubMenuModel::CommandIdToTabVectorIndex(
627 int command_id,
628 TabNavigationItems** tab_items) {
629 DCHECK(IsTabModelCommandId(command_id));
630 if (command_id >= kFirstOtherDevicesTabCommandId) {
631 *tab_items = &other_devices_tab_navigation_items_;
632 return command_id - kFirstOtherDevicesTabCommandId;
634 *tab_items = &local_tab_navigation_items_;
635 return command_id - kFirstLocalTabCommandId;
638 void RecentTabsSubMenuModel::ClearLocalEntries() {
639 // Remove local items (recently closed tabs and windows) from menumodel.
640 while (last_local_model_index_ > kHistorySeparatorIndex)
641 RemoveItemAt(last_local_model_index_--);
643 // Cancel asynchronous FaviconService::GetFaviconImageForPageURL() tasks of
644 // all local tabs.
645 local_tab_cancelable_task_tracker_.TryCancelAll();
647 // Remove all local tab navigation items.
648 local_tab_navigation_items_.clear();
650 // Remove all local window items.
651 local_window_items_.clear();
654 sync_driver::OpenTabsUIDelegate*
655 RecentTabsSubMenuModel::GetOpenTabsUIDelegate() {
656 if (!open_tabs_delegate_) {
657 ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
658 GetForProfile(browser_->profile());
659 // Only return the delegate if it exists and it is done syncing sessions.
660 if (service && service->IsSyncActive())
661 open_tabs_delegate_ = service->GetOpenTabsUIDelegate();
663 return open_tabs_delegate_;
666 void RecentTabsSubMenuModel::TabRestoreServiceChanged(
667 TabRestoreService* service) {
668 ClearLocalEntries();
670 BuildLocalEntries();
672 ui::MenuModelDelegate* menu_model_delegate = GetMenuModelDelegate();
673 if (menu_model_delegate)
674 menu_model_delegate->OnMenuStructureChanged();
677 void RecentTabsSubMenuModel::TabRestoreServiceDestroyed(
678 TabRestoreService* service) {
679 TabRestoreServiceChanged(service);