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/glue/synced_session.h"
21 #include "chrome/browser/sync/open_tabs_ui_delegate.h"
22 #include "chrome/browser/sync/profile_sync_service.h"
23 #include "chrome/browser/sync/profile_sync_service_factory.h"
24 #include "chrome/browser/ui/browser.h"
25 #include "chrome/browser/ui/browser_commands.h"
26 #include "chrome/browser/ui/tabs/tab_strip_model.h"
27 #include "chrome/browser/ui/toolbar/wrench_menu_model.h"
28 #include "chrome/common/pref_names.h"
29 #include "components/favicon_base/favicon_types.h"
30 #include "grit/browser_resources.h"
31 #include "grit/generated_resources.h"
32 #include "grit/theme_resources.h"
33 #include "grit/ui_resources.h"
34 #include "ui/base/accelerators/accelerator.h"
35 #include "ui/base/l10n/l10n_util.h"
36 #include "ui/base/resource/resource_bundle.h"
37 #include "ui/gfx/favicon_size.h"
40 #include "ash/accelerators/accelerator_table.h"
41 #endif // defined(USE_ASH)
45 // Initial comamnd ID's for navigatable (and hence executable) tab/window menu
46 // items. The menumodel and storage structures are not 1-1:
47 // - menumodel has "Recently closed" header, "No tabs from other devices",
48 // device section headers, separators, local and other devices' tab items, and
49 // local window items.
50 // - |local_tab_navigation_items_| and |other_devices_tab_navigation_items_|
51 // only have navigatabale/executable tab items.
52 // - |local_window_items_| only has executable open window items.
53 // Using initial command IDs for local tab, local window and other devices' tab
54 // items makes it easier and less error-prone to manipulate the menumodel and
55 // storage structures. These ids must be bigger than the maximum possible
56 // number of items in the menumodel, so that index of the last menu item doesn't
57 // clash with these values when menu items are retrieved via
58 // GetIndexOfCommandId().
59 // The range of all command ID's used in RecentTabsSubMenuModel, including the
60 // "Recently closed" headers, must be between
61 // |WrenchMenuModel::kMinRecentTabsCommandId| i.e. 1001 and 1200
62 // (|WrenchMenuModel::kMaxRecentTabsCommandId|) inclusively.
63 const int kFirstLocalTabCommandId
= WrenchMenuModel::kMinRecentTabsCommandId
;
64 const int kFirstLocalWindowCommandId
= 1031;
65 const int kFirstOtherDevicesTabCommandId
= 1051;
66 const int kMinDeviceNameCommandId
= 1100;
67 const int kMaxDeviceNameCommandId
= 1110;
69 // The maximum number of local recently closed entries (tab or window) to be
71 const int kMaxLocalEntries
= 8;
73 // Comparator function for use with std::sort that will sort sessions by
74 // descending modified_time (i.e., most recent first).
75 bool SortSessionsByRecency(const browser_sync::SyncedSession
* s1
,
76 const browser_sync::SyncedSession
* s2
) {
77 return s1
->modified_time
> s2
->modified_time
;
80 // Comparator function for use with std::sort that will sort tabs by
81 // descending timestamp (i.e., most recent first).
82 bool SortTabsByRecency(const SessionTab
* t1
, const SessionTab
* t2
) {
83 return t1
->timestamp
> t2
->timestamp
;
86 // Returns true if the command id identifies a tab menu item.
87 bool IsTabModelCommandId(int command_id
) {
88 return ((command_id
>= kFirstLocalTabCommandId
&&
89 command_id
< kFirstLocalWindowCommandId
) ||
90 (command_id
>= kFirstOtherDevicesTabCommandId
&&
91 command_id
< kMinDeviceNameCommandId
));
94 // Returns true if the command id identifies a window menu item.
95 bool IsWindowModelCommandId(int command_id
) {
96 return command_id
>= kFirstLocalWindowCommandId
&&
97 command_id
< kFirstOtherDevicesTabCommandId
;
100 bool IsDeviceNameCommandId(int command_id
) {
101 return command_id
>= kMinDeviceNameCommandId
&&
102 command_id
<= kMaxDeviceNameCommandId
;
105 // Convert |tab_vector_index| to command id of menu item, with
106 // |first_command_id| as the base command id.
107 int TabVectorIndexToCommandId(int tab_vector_index
, int first_command_id
) {
108 int command_id
= tab_vector_index
+ first_command_id
;
109 DCHECK(IsTabModelCommandId(command_id
));
113 // Convert |window_vector_index| to command id of menu item.
114 int WindowVectorIndexToCommandId(int window_vector_index
) {
115 int command_id
= window_vector_index
+ kFirstLocalWindowCommandId
;
116 DCHECK(IsWindowModelCommandId(command_id
));
120 // Convert |command_id| of menu item to index in |local_window_items_|.
121 int CommandIdToWindowVectorIndex(int command_id
) {
122 DCHECK(IsWindowModelCommandId(command_id
));
123 return command_id
- kFirstLocalWindowCommandId
;
128 enum RecentTabAction
{
129 LOCAL_SESSION_TAB
= 0,
133 LIMIT_RECENT_TAB_ACTION
136 // An element in |RecentTabsSubMenuModel::local_tab_navigation_items_| or
137 // |RecentTabsSubMenuModel::other_devices_tab_navigation_items_| that stores
138 // the navigation information of a local or other devices' tab required to
140 struct RecentTabsSubMenuModel::TabNavigationItem
{
141 TabNavigationItem() : tab_id(-1) {}
143 TabNavigationItem(const std::string
& session_tag
,
144 const SessionID::id_type
& tab_id
,
145 const base::string16
& title
,
147 : session_tag(session_tag
),
152 // For use by std::set for sorting.
153 bool operator<(const TabNavigationItem
& other
) const {
154 return url
< other
.url
;
157 // Empty for local tabs, non-empty for other devices' tabs.
158 std::string session_tag
;
159 SessionID::id_type tab_id
; // -1 for invalid, >= 0 otherwise.
160 base::string16 title
;
164 const int RecentTabsSubMenuModel::kRecentlyClosedHeaderCommandId
= 1120;
165 const int RecentTabsSubMenuModel::kDisabledRecentlyClosedHeaderCommandId
= 1121;
167 RecentTabsSubMenuModel::RecentTabsSubMenuModel(
168 ui::AcceleratorProvider
* accelerator_provider
,
170 browser_sync::OpenTabsUIDelegate
* open_tabs_delegate
)
171 : ui::SimpleMenuModel(this),
173 open_tabs_delegate_(open_tabs_delegate
),
174 last_local_model_index_(-1),
175 default_favicon_(ui::ResourceBundle::GetSharedInstance().
176 GetNativeImageNamed(IDR_DEFAULT_FAVICON
)),
177 weak_ptr_factory_(this) {
178 // Invoke asynchronous call to load tabs from local last session, which does
179 // nothing if the tabs have already been loaded or they shouldn't be loaded.
180 // TabRestoreServiceChanged() will be called after the tabs are loaded.
181 TabRestoreService
* service
=
182 TabRestoreServiceFactory::GetForProfile(browser_
->profile());
184 service
->LoadTabsFromLastSession();
186 // TODO(sail): enable this when mac implements the dynamic menu, together with
187 // MenuModelDelegate::MenuStructureChanged().
188 #if !defined(OS_MACOSX)
189 service
->AddObserver(this);
195 // Retrieve accelerator key for IDC_RESTORE_TAB now, because on ASH, it's not
196 // defined in |accelerator_provider|, but in shell, so simply retrieve it now
197 // for all ASH and non-ASH for use in |GetAcceleratorForCommandId|.
199 for (size_t i
= 0; i
< ash::kAcceleratorDataLength
; ++i
) {
200 const ash::AcceleratorData
& accel_data
= ash::kAcceleratorData
[i
];
201 if (accel_data
.action
== ash::RESTORE_TAB
) {
202 reopen_closed_tab_accelerator_
= ui::Accelerator(accel_data
.keycode
,
203 accel_data
.modifiers
);
208 if (accelerator_provider
) {
209 accelerator_provider
->GetAcceleratorForCommandId(
210 IDC_RESTORE_TAB
, &reopen_closed_tab_accelerator_
);
212 #endif // defined(USE_ASH)
215 RecentTabsSubMenuModel::~RecentTabsSubMenuModel() {
216 TabRestoreService
* service
=
217 TabRestoreServiceFactory::GetForProfile(browser_
->profile());
219 service
->RemoveObserver(this);
222 bool RecentTabsSubMenuModel::IsCommandIdChecked(int command_id
) const {
226 bool RecentTabsSubMenuModel::IsCommandIdEnabled(int command_id
) const {
227 if (command_id
== kRecentlyClosedHeaderCommandId
||
228 command_id
== kDisabledRecentlyClosedHeaderCommandId
||
229 command_id
== IDC_RECENT_TABS_NO_DEVICE_TABS
||
230 IsDeviceNameCommandId(command_id
)) {
236 bool RecentTabsSubMenuModel::GetAcceleratorForCommandId(
237 int command_id
, ui::Accelerator
* accelerator
) {
238 // If there are no recently closed items, we show the accelerator beside
239 // the header, otherwise, we show it beside the first item underneath it.
240 int index_in_menu
= GetIndexOfCommandId(command_id
);
241 int header_index
= GetIndexOfCommandId(kRecentlyClosedHeaderCommandId
);
242 if ((command_id
== kDisabledRecentlyClosedHeaderCommandId
||
243 (header_index
!= -1 && index_in_menu
== header_index
+ 1)) &&
244 reopen_closed_tab_accelerator_
.key_code() != ui::VKEY_UNKNOWN
) {
245 *accelerator
= reopen_closed_tab_accelerator_
;
251 void RecentTabsSubMenuModel::ExecuteCommand(int command_id
, int event_flags
) {
252 if (command_id
== IDC_SHOW_HISTORY
) {
253 UMA_HISTOGRAM_ENUMERATION("WrenchMenu.RecentTabsSubMenu", SHOW_MORE
,
254 LIMIT_RECENT_TAB_ACTION
);
255 // We show all "other devices" on the history page.
256 chrome::ExecuteCommandWithDisposition(browser_
, IDC_SHOW_HISTORY
,
257 ui::DispositionFromEventFlags(event_flags
));
261 DCHECK_NE(IDC_RECENT_TABS_NO_DEVICE_TABS
, command_id
);
262 DCHECK(!IsDeviceNameCommandId(command_id
));
264 WindowOpenDisposition disposition
=
265 ui::DispositionFromEventFlags(event_flags
);
266 if (disposition
== CURRENT_TAB
) // Force to open a new foreground tab.
267 disposition
= NEW_FOREGROUND_TAB
;
269 TabRestoreService
* service
=
270 TabRestoreServiceFactory::GetForProfile(browser_
->profile());
271 TabRestoreServiceDelegate
* delegate
=
272 TabRestoreServiceDelegate::FindDelegateForWebContents(
273 browser_
->tab_strip_model()->GetActiveWebContents());
274 if (IsTabModelCommandId(command_id
)) {
275 TabNavigationItems
* tab_items
= NULL
;
276 int tab_items_idx
= CommandIdToTabVectorIndex(command_id
, &tab_items
);
277 const TabNavigationItem
& item
= (*tab_items
)[tab_items_idx
];
278 DCHECK(item
.tab_id
> -1 && item
.url
.is_valid());
280 if (item
.session_tag
.empty()) { // Restore tab of local session.
281 if (service
&& delegate
) {
282 UMA_HISTOGRAM_ENUMERATION("WrenchMenu.RecentTabsSubMenu",
283 LOCAL_SESSION_TAB
, LIMIT_RECENT_TAB_ACTION
);
284 service
->RestoreEntryById(delegate
, item
.tab_id
,
285 browser_
->host_desktop_type(), disposition
);
287 } else { // Restore tab of session from other devices.
288 browser_sync::OpenTabsUIDelegate
* open_tabs
= GetOpenTabsUIDelegate();
291 const SessionTab
* tab
;
292 if (!open_tabs
->GetForeignTab(item
.session_tag
, item
.tab_id
, &tab
))
294 if (tab
->navigations
.empty())
296 UMA_HISTOGRAM_ENUMERATION("WrenchMenu.RecentTabsSubMenu",
297 OTHER_DEVICE_TAB
, LIMIT_RECENT_TAB_ACTION
);
298 SessionRestore::RestoreForeignSessionTab(
299 browser_
->tab_strip_model()->GetActiveWebContents(),
303 DCHECK(IsWindowModelCommandId(command_id
));
304 if (service
&& delegate
) {
305 int window_items_idx
= CommandIdToWindowVectorIndex(command_id
);
306 DCHECK(window_items_idx
>= 0 &&
307 window_items_idx
< static_cast<int>(local_window_items_
.size()));
308 UMA_HISTOGRAM_ENUMERATION("WrenchMenu.RecentTabsSubMenu", RESTORE_WINDOW
,
309 LIMIT_RECENT_TAB_ACTION
);
310 service
->RestoreEntryById(delegate
, local_window_items_
[window_items_idx
],
311 browser_
->host_desktop_type(), disposition
);
316 int RecentTabsSubMenuModel::GetFirstRecentTabsCommandId() {
317 return WindowVectorIndexToCommandId(0);
320 const gfx::FontList
* RecentTabsSubMenuModel::GetLabelFontListAt(
322 int command_id
= GetCommandIdAt(index
);
323 if (command_id
== kRecentlyClosedHeaderCommandId
||
324 IsDeviceNameCommandId(command_id
)) {
325 return &ui::ResourceBundle::GetSharedInstance().GetFontList(
326 ui::ResourceBundle::BoldFont
);
331 int RecentTabsSubMenuModel::GetMaxWidthForItemAtIndex(int item_index
) const {
332 int command_id
= GetCommandIdAt(item_index
);
333 if (command_id
== IDC_RECENT_TABS_NO_DEVICE_TABS
||
334 command_id
== kRecentlyClosedHeaderCommandId
||
335 command_id
== kDisabledRecentlyClosedHeaderCommandId
) {
341 bool RecentTabsSubMenuModel::GetURLAndTitleForItemAtIndex(
344 base::string16
* title
) {
345 int command_id
= GetCommandIdAt(index
);
346 if (IsTabModelCommandId(command_id
)) {
347 TabNavigationItems
* tab_items
= NULL
;
348 int tab_items_idx
= CommandIdToTabVectorIndex(command_id
, &tab_items
);
349 const TabNavigationItem
& item
= (*tab_items
)[tab_items_idx
];
350 *url
= item
.url
.possibly_invalid_spec();
357 void RecentTabsSubMenuModel::Build() {
358 // The menu contains:
359 // - Recently closed header, then list of local recently closed tabs/windows,
361 // - device 1 section header, then list of tabs from device, then separator
362 // - device 2 section header, then list of tabs from device, then separator
363 // - device 3 section header, then list of tabs from device, then separator
364 // - More... to open the history tab to get more other devices.
365 // |local_tab_navigation_items_| and |other_devices_tab_navigation_items_|
366 // only contain navigatable (and hence executable) tab items for local
367 // recently closed tabs and tabs from other devices respectively.
368 // |local_window_items_| contains the local recently closed windows.
370 BuildTabsFromOtherDevices();
373 void RecentTabsSubMenuModel::BuildLocalEntries() {
374 // All local items use InsertItem*At() to append or insert a menu item.
375 // We're appending if building the entries for the first time i.e. invoked
376 // from Constructor(), inserting when local entries change subsequently i.e.
377 // invoked from TabRestoreServiceChanged().
379 DCHECK_EQ(last_local_model_index_
, -1);
381 TabRestoreService
* service
=
382 TabRestoreServiceFactory::GetForProfile(browser_
->profile());
383 if (!service
|| service
->entries().size() == 0) {
384 // This is to show a disabled restore tab entry with the accelerator to
385 // teach users about this command.
386 InsertItemWithStringIdAt(++last_local_model_index_
,
387 kDisabledRecentlyClosedHeaderCommandId
,
388 IDS_NEW_TAB_RECENTLY_CLOSED
);
390 InsertItemWithStringIdAt(++last_local_model_index_
,
391 kRecentlyClosedHeaderCommandId
,
392 IDS_NEW_TAB_RECENTLY_CLOSED
);
393 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
394 SetIcon(last_local_model_index_
,
395 rb
.GetNativeImageNamed(IDR_RECENTLY_CLOSED_WINDOW
));
398 TabRestoreService::Entries entries
= service
->entries();
399 for (TabRestoreService::Entries::const_iterator it
= entries
.begin();
400 it
!= entries
.end() && added_count
< kMaxLocalEntries
; ++it
) {
401 TabRestoreService::Entry
* entry
= *it
;
402 if (entry
->type
== TabRestoreService::TAB
) {
403 TabRestoreService::Tab
* tab
=
404 static_cast<TabRestoreService::Tab
*>(entry
);
405 const sessions::SerializedNavigationEntry
& current_navigation
=
406 tab
->navigations
.at(tab
->current_navigation_index
);
409 current_navigation
.title(),
410 current_navigation
.virtual_url(),
411 ++last_local_model_index_
);
413 DCHECK_EQ(entry
->type
, TabRestoreService::WINDOW
);
414 BuildLocalWindowItem(
416 static_cast<TabRestoreService::Window
*>(entry
)->tabs
.size(),
417 ++last_local_model_index_
);
423 DCHECK_GE(last_local_model_index_
, 0);
426 void RecentTabsSubMenuModel::BuildTabsFromOtherDevices() {
427 // All other devices' items (device headers or tabs) use AddItem*() to append
428 // a menu item, because they are always only built once (i.e. invoked from
429 // Constructor()) and don't change after that.
431 browser_sync::OpenTabsUIDelegate
* open_tabs
= GetOpenTabsUIDelegate();
432 std::vector
<const browser_sync::SyncedSession
*> sessions
;
433 if (!open_tabs
|| !open_tabs
->GetAllForeignSessions(&sessions
)) {
434 AddSeparator(ui::NORMAL_SEPARATOR
);
435 AddItemWithStringId(IDC_RECENT_TABS_NO_DEVICE_TABS
,
436 IDS_RECENT_TABS_NO_DEVICE_TABS
);
440 // Sort sessions from most recent to least recent.
441 std::sort(sessions
.begin(), sessions
.end(), SortSessionsByRecency
);
443 const size_t kMaxSessionsToShow
= 3;
444 size_t num_sessions_added
= 0;
446 i
< sessions
.size() && num_sessions_added
< kMaxSessionsToShow
; ++i
) {
447 const browser_sync::SyncedSession
* session
= sessions
[i
];
448 const std::string
& session_tag
= session
->session_tag
;
450 // Get windows of session.
451 std::vector
<const SessionWindow
*> windows
;
452 if (!open_tabs
->GetForeignSession(session_tag
, &windows
) ||
457 // Collect tabs from all windows of session, pruning those that are not
458 // syncable or are NewTabPage, then sort them from most recent to least
459 // recent, independent of which window the tabs were from.
460 std::vector
<const SessionTab
*> tabs_in_session
;
461 for (size_t j
= 0; j
< windows
.size(); ++j
) {
462 const SessionWindow
* window
= windows
[j
];
463 for (size_t t
= 0; t
< window
->tabs
.size(); ++t
) {
464 const SessionTab
* tab
= window
->tabs
[t
];
465 if (tab
->navigations
.empty())
467 const sessions::SerializedNavigationEntry
& current_navigation
=
468 tab
->navigations
.at(tab
->normalized_navigation_index());
469 if (chrome::IsNTPURL(current_navigation
.virtual_url(),
470 browser_
->profile())) {
473 tabs_in_session
.push_back(tab
);
476 if (tabs_in_session
.empty())
478 std::sort(tabs_in_session
.begin(), tabs_in_session
.end(),
481 // Add the header for the device session.
482 DCHECK(!session
->session_name
.empty());
483 AddSeparator(ui::NORMAL_SEPARATOR
);
484 int command_id
= kMinDeviceNameCommandId
+ i
;
485 DCHECK_LE(command_id
, kMaxDeviceNameCommandId
);
486 AddItem(command_id
, base::UTF8ToUTF16(session
->session_name
));
487 AddDeviceFavicon(GetItemCount() - 1, session
->device_type
);
489 // Build tab menu items from sorted session tabs.
490 const size_t kMaxTabsPerSessionToShow
= 4;
492 k
< std::min(tabs_in_session
.size(), kMaxTabsPerSessionToShow
);
494 BuildOtherDevicesTabItem(session_tag
, *tabs_in_session
[k
]);
495 } // for all tabs in one session
497 ++num_sessions_added
;
498 } // for all sessions
500 // We are not supposed to get here unless at least some items were added.
501 DCHECK_GT(GetItemCount(), 0);
502 AddSeparator(ui::NORMAL_SEPARATOR
);
503 AddItemWithStringId(IDC_SHOW_HISTORY
, IDS_RECENT_TABS_MORE
);
506 void RecentTabsSubMenuModel::BuildLocalTabItem(int session_id
,
507 const base::string16
& title
,
509 int curr_model_index
) {
510 TabNavigationItem
item(std::string(), session_id
, title
, url
);
511 int command_id
= TabVectorIndexToCommandId(
512 local_tab_navigation_items_
.size(), kFirstLocalTabCommandId
);
513 // See comments in BuildLocalEntries() about usage of InsertItem*At().
514 // There may be no tab title, in which case, use the url as tab title.
515 InsertItemAt(curr_model_index
, command_id
,
516 title
.empty() ? base::UTF8ToUTF16(item
.url
.spec()) : title
);
517 AddTabFavicon(command_id
, item
.url
);
518 local_tab_navigation_items_
.push_back(item
);
521 void RecentTabsSubMenuModel::BuildLocalWindowItem(
522 const SessionID::id_type
& window_id
,
524 int curr_model_index
) {
525 int command_id
= WindowVectorIndexToCommandId(local_window_items_
.size());
526 // See comments in BuildLocalEntries() about usage of InsertItem*At().
528 InsertItemWithStringIdAt(curr_model_index
, command_id
,
529 IDS_NEW_TAB_RECENTLY_CLOSED_WINDOW_SINGLE
);
531 InsertItemAt(curr_model_index
, command_id
, l10n_util::GetStringFUTF16(
532 IDS_NEW_TAB_RECENTLY_CLOSED_WINDOW_MULTIPLE
,
533 base::IntToString16(num_tabs
)));
535 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
536 SetIcon(curr_model_index
, rb
.GetNativeImageNamed(IDR_RECENTLY_CLOSED_WINDOW
));
537 local_window_items_
.push_back(window_id
);
540 void RecentTabsSubMenuModel::BuildOtherDevicesTabItem(
541 const std::string
& session_tag
,
542 const SessionTab
& tab
) {
543 const sessions::SerializedNavigationEntry
& current_navigation
=
544 tab
.navigations
.at(tab
.normalized_navigation_index());
545 TabNavigationItem
item(session_tag
, tab
.tab_id
.id(),
546 current_navigation
.title(),
547 current_navigation
.virtual_url());
548 int command_id
= TabVectorIndexToCommandId(
549 other_devices_tab_navigation_items_
.size(),
550 kFirstOtherDevicesTabCommandId
);
551 // See comments in BuildTabsFromOtherDevices() about usage of AddItem*().
552 // There may be no tab title, in which case, use the url as tab title.
554 current_navigation
.title().empty() ?
555 base::UTF8ToUTF16(item
.url
.spec()) : current_navigation
.title());
556 AddTabFavicon(command_id
, item
.url
);
557 other_devices_tab_navigation_items_
.push_back(item
);
560 void RecentTabsSubMenuModel::AddDeviceFavicon(
562 browser_sync::SyncedSession::DeviceType device_type
) {
564 switch (device_type
) {
565 case browser_sync::SyncedSession::TYPE_PHONE
:
566 favicon_id
= IDR_PHONE_FAVICON
;
569 case browser_sync::SyncedSession::TYPE_TABLET
:
570 favicon_id
= IDR_TABLET_FAVICON
;
573 case browser_sync::SyncedSession::TYPE_CHROMEOS
:
574 case browser_sync::SyncedSession::TYPE_WIN
:
575 case browser_sync::SyncedSession::TYPE_MACOSX
:
576 case browser_sync::SyncedSession::TYPE_LINUX
:
577 case browser_sync::SyncedSession::TYPE_OTHER
:
578 case browser_sync::SyncedSession::TYPE_UNSET
:
579 favicon_id
= IDR_LAPTOP_FAVICON
;
583 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
584 SetIcon(index_in_menu
, rb
.GetNativeImageNamed(favicon_id
));
587 void RecentTabsSubMenuModel::AddTabFavicon(int command_id
, const GURL
& url
) {
588 bool is_local_tab
= command_id
< kFirstOtherDevicesTabCommandId
;
589 int index_in_menu
= GetIndexOfCommandId(command_id
);
592 // If tab has synced favicon, use it.
593 // Note that currently, other devices' tabs only have favicons if
594 // --sync-tab-favicons switch is on; according to zea@, this flag is now
595 // automatically enabled for iOS and android, and they're looking into
596 // enabling it for other platforms.
597 browser_sync::OpenTabsUIDelegate
* open_tabs
= GetOpenTabsUIDelegate();
598 scoped_refptr
<base::RefCountedMemory
> favicon_png
;
600 open_tabs
->GetSyncedFaviconForPageURL(url
.spec(), &favicon_png
)) {
601 gfx::Image image
= gfx::Image::CreateFrom1xPNGBytes(favicon_png
);
602 SetIcon(index_in_menu
, image
);
607 // Otherwise, start to fetch the favicon from local history asynchronously.
608 // Set default icon first.
609 SetIcon(index_in_menu
, default_favicon_
);
610 // Start request to fetch actual icon if possible.
611 FaviconService
* favicon_service
= FaviconServiceFactory::GetForProfile(
612 browser_
->profile(), Profile::EXPLICIT_ACCESS
);
613 if (!favicon_service
)
616 favicon_service
->GetFaviconImageForURL(
617 FaviconService::FaviconForURLParams(
618 url
, favicon_base::FAVICON
, gfx::kFaviconSize
),
619 base::Bind(&RecentTabsSubMenuModel::OnFaviconDataAvailable
,
620 weak_ptr_factory_
.GetWeakPtr(),
622 is_local_tab
? &local_tab_cancelable_task_tracker_
623 : &other_devices_tab_cancelable_task_tracker_
);
626 void RecentTabsSubMenuModel::OnFaviconDataAvailable(
628 const favicon_base::FaviconImageResult
& image_result
) {
629 if (image_result
.image
.IsEmpty())
631 int index_in_menu
= GetIndexOfCommandId(command_id
);
632 DCHECK_GT(index_in_menu
, -1);
633 SetIcon(index_in_menu
, image_result
.image
);
634 ui::MenuModelDelegate
* menu_model_delegate
= GetMenuModelDelegate();
635 if (menu_model_delegate
)
636 menu_model_delegate
->OnIconChanged(index_in_menu
);
639 int RecentTabsSubMenuModel::CommandIdToTabVectorIndex(
641 TabNavigationItems
** tab_items
) {
642 DCHECK(IsTabModelCommandId(command_id
));
643 if (command_id
>= kFirstOtherDevicesTabCommandId
) {
644 *tab_items
= &other_devices_tab_navigation_items_
;
645 return command_id
- kFirstOtherDevicesTabCommandId
;
647 *tab_items
= &local_tab_navigation_items_
;
648 return command_id
- kFirstLocalTabCommandId
;
651 void RecentTabsSubMenuModel::ClearLocalEntries() {
652 // Remove local items (recently closed tabs and windows) from menumodel.
653 while (last_local_model_index_
>= 0)
654 RemoveItemAt(last_local_model_index_
--);
656 // Cancel asynchronous FaviconService::GetFaviconImageForURL() tasks of all
658 local_tab_cancelable_task_tracker_
.TryCancelAll();
660 // Remove all local tab navigation items.
661 local_tab_navigation_items_
.clear();
663 // Remove all local window items.
664 local_window_items_
.clear();
667 browser_sync::OpenTabsUIDelegate
*
668 RecentTabsSubMenuModel::GetOpenTabsUIDelegate() {
669 if (!open_tabs_delegate_
) {
670 ProfileSyncService
* service
= ProfileSyncServiceFactory::GetInstance()->
671 GetForProfile(browser_
->profile());
672 // Only return the delegate if it exists and it is done syncing sessions.
673 if (service
&& service
->ShouldPushChanges())
674 open_tabs_delegate_
= service
->GetOpenTabsUIDelegate();
676 return open_tabs_delegate_
;
679 void RecentTabsSubMenuModel::TabRestoreServiceChanged(
680 TabRestoreService
* service
) {
685 ui::MenuModelDelegate
* menu_model_delegate
= GetMenuModelDelegate();
686 if (menu_model_delegate
)
687 menu_model_delegate
->OnMenuStructureChanged();
690 void RecentTabsSubMenuModel::TabRestoreServiceDestroyed(
691 TabRestoreService
* service
) {
692 TabRestoreServiceChanged(service
);