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"
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"
39 #include "ash/accelerators/accelerator_table.h"
40 #endif // defined(USE_ASH)
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
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
));
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
));
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
;
125 enum RecentTabAction
{
126 LOCAL_SESSION_TAB
= 0,
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
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
,
144 : session_tag(session_tag
),
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
;
161 const int RecentTabsSubMenuModel::kRecentlyClosedHeaderCommandId
= 1120;
162 const int RecentTabsSubMenuModel::kDisabledRecentlyClosedHeaderCommandId
= 1121;
164 RecentTabsSubMenuModel::RecentTabsSubMenuModel(
165 ui::AcceleratorProvider
* accelerator_provider
,
167 sync_driver::OpenTabsUIDelegate
* open_tabs_delegate
)
168 : ui::SimpleMenuModel(this),
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());
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);
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|.
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
);
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());
221 service
->RemoveObserver(this);
224 bool RecentTabsSubMenuModel::IsCommandIdChecked(int command_id
) const {
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
)) {
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_
;
251 if (command_id
== IDC_SHOW_HISTORY
) {
252 *accelerator
= show_history_accelerator_
;
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
));
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();
301 const sessions::SessionTab
* tab
;
302 if (!open_tabs
->GetForeignTab(item
.session_tag
, item
.tab_id
, &tab
))
304 if (tab
->navigations
.empty())
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(),
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
,
334 int RecentTabsSubMenuModel::GetFirstRecentTabsCommandId() {
335 return WindowVectorIndexToCommandId(0);
338 const gfx::FontList
* RecentTabsSubMenuModel::GetLabelFontListAt(
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
);
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
) {
359 bool RecentTabsSubMenuModel::GetURLAndTitleForItemAtIndex(
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();
375 void RecentTabsSubMenuModel::Build() {
376 // The menu contains:
377 // - History to open the full history tab.
379 // - Recently closed header, then list of local recently closed tabs/windows,
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
);
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
);
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
));
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
);
430 current_navigation
.title(),
431 current_navigation
.virtual_url(),
432 ++last_local_model_index_
);
434 DCHECK_EQ(entry
->type
, TabRestoreService::WINDOW
);
435 BuildLocalWindowItem(
437 static_cast<TabRestoreService::Window
*>(entry
)->tabs
.size(),
438 ++last_local_model_index_
);
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
);
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;
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())
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;
487 k
< std::min(tabs_in_session
.size(), kMaxTabsPerSessionToShow
);
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
,
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
,
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.
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(
549 sync_driver::SyncedSession::DeviceType device_type
) {
551 switch (device_type
) {
552 case sync_driver::SyncedSession::TYPE_PHONE
:
553 favicon_id
= IDR_PHONE_FAVICON
;
556 case sync_driver::SyncedSession::TYPE_TABLET
:
557 favicon_id
= IDR_TABLET_FAVICON
;
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
;
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
);
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
;
587 open_tabs
->GetSyncedFaviconForPageURL(url
.spec(), &favicon_png
)) {
588 gfx::Image image
= gfx::Image::CreateFrom1xPNGBytes(favicon_png
);
589 SetIcon(index_in_menu
, image
);
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
)
604 favicon_service
->GetFaviconImageForPageURL(
606 base::Bind(&RecentTabsSubMenuModel::OnFaviconDataAvailable
,
607 weak_ptr_factory_
.GetWeakPtr(),
609 is_local_tab
? &local_tab_cancelable_task_tracker_
610 : &other_devices_tab_cancelable_task_tracker_
);
613 void RecentTabsSubMenuModel::OnFaviconDataAvailable(
615 const favicon_base::FaviconImageResult
& image_result
) {
616 if (image_result
.image
.IsEmpty())
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(
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
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
) {
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
);