Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(275)

Side by Side Diff: chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.cc

Issue 23530070: backup for dynamic recent tabs submenu (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: new RecentTabsMenuModelDelegate w/ new intf Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.h" 5 #include "chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/metrics/histogram.h" 8 #include "base/metrics/histogram.h"
9 #include "base/strings/string_number_conversions.h" 9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/utf_string_conversions.h" 10 #include "base/strings/utf_string_conversions.h"
(...skipping 23 matching lines...) Expand all
34 #include "ui/base/l10n/l10n_util.h" 34 #include "ui/base/l10n/l10n_util.h"
35 #include "ui/base/resource/resource_bundle.h" 35 #include "ui/base/resource/resource_bundle.h"
36 #include "ui/gfx/favicon_size.h" 36 #include "ui/gfx/favicon_size.h"
37 37
38 #if defined(USE_ASH) 38 #if defined(USE_ASH)
39 #include "ash/accelerators/accelerator_table.h" 39 #include "ash/accelerators/accelerator_table.h"
40 #endif // defined(USE_ASH) 40 #endif // defined(USE_ASH)
41 41
42 namespace { 42 namespace {
43 43
44 // First comamnd id for navigatable (and hence executable) tab menu item. 44 // First comamnd id for navigatable (and hence executable) tab/window menu item.
45 // The models and menu are not 1-1: 45 // The models and menu are not 1-1:
46 // - menu has "Reopen closed tab", "No tabs from other devices", device section 46 // - menu has "Recently closed" header, "No tabs from other devices", device
47 // headers, separators and executable tab items. 47 // section headers, separators, executable local and foreign tab items, and
48 // - |tab_navigation_items_| only has navigatabale/executable tab items. 48 // executable local window items.
49 // - |window_items_| only has executable open window items. 49 // - |local_tab_navigation_items_| and |foreign_tab_navigation_items_| only have
50 // Using an initial command ids for tab/window items makes it easier and less 50 // navigatabale/executable tab items.
51 // error-prone to manipulate the models and menu. 51 // - |local_window_items_| only has executable open window items.
52 // These values must be bigger than the maximum possible number of items in 52 // Using initial command ids for local tab, local window and foreign tab items
53 // menu, so that index of last menu item doesn't clash with this value when menu 53 // makes it easier and less error-prone to manipulate the models, data vectors
54 // items are retrieved via GetIndexOfCommandId. 54 // and menu. These ids must be bigger than the maximum possible number of
55 const int kFirstTabCommandId = 100; 55 // items in menu, so that index of the last menu item doesn't clash with these
56 const int kFirstWindowCommandId = 200; 56 // values when menu items are retrieved via GetIndexOfCommandId.
57 const int kFirstLocalTabCommandId = 100;
58 const int kFirstLocalWindowCommandId = 200;
59 const int kFirstForeignTabCommandId = 300;
57 60
58 // The maximum number of recently closed entries to be shown in the menu. 61 // The maximum number of local recently closed entries (tab or window) to be
59 const int kMaxRecentlyClosedEntries = 8; 62 // shown in the menu.
63 const int kMaxLocalEntries = 8;
60 64
61 // Comparator function for use with std::sort that will sort sessions by 65 // Comparator function for use with std::sort that will sort sessions by
62 // descending modified_time (i.e., most recent first). 66 // descending modified_time (i.e., most recent first).
63 bool SortSessionsByRecency(const browser_sync::SyncedSession* s1, 67 bool SortSessionsByRecency(const browser_sync::SyncedSession* s1,
64 const browser_sync::SyncedSession* s2) { 68 const browser_sync::SyncedSession* s2) {
65 return s1->modified_time > s2->modified_time; 69 return s1->modified_time > s2->modified_time;
66 } 70 }
67 71
68 // Comparator function for use with std::sort that will sort tabs by 72 // Comparator function for use with std::sort that will sort tabs by
69 // descending timestamp (i.e., most recent first). 73 // descending timestamp (i.e., most recent first).
70 bool SortTabsByRecency(const SessionTab* t1, const SessionTab* t2) { 74 bool SortTabsByRecency(const SessionTab* t1, const SessionTab* t2) {
71 return t1->timestamp > t2->timestamp; 75 return t1->timestamp > t2->timestamp;
72 } 76 }
73 77
74 // Returns true if the command id is related to a tab model index. 78 // Returns true if the command id identifies a tab model index.
75 bool IsTabModelCommandId(int command_id) { 79 bool IsTabModelCommandId(int command_id) {
76 return command_id >= kFirstTabCommandId && command_id < kFirstWindowCommandId; 80 return command_id >= kFirstForeignTabCommandId ||
81 (command_id >= kFirstLocalTabCommandId &&
82 command_id < kFirstLocalWindowCommandId);
77 } 83 }
78 84
79 // Returns true if the command id is related to a window model index. 85 // Returns true if the command id identifies a window model index.
80 bool IsWindowModelCommandId(int command_id) { 86 bool IsWindowModelCommandId(int command_id) {
81 return command_id >= kFirstWindowCommandId && 87 return command_id >= kFirstLocalWindowCommandId &&
82 command_id < RecentTabsSubMenuModel::kRecentlyClosedHeaderCommandId; 88 command_id < RecentTabsSubMenuModel::kRecentlyClosedHeaderCommandId;
83 } 89 }
84 90
85 // Convert |tab_model_index| to command id of menu item. 91 // Convert |tab_model_index| to command id of menu item, with |first_command_id|
86 int TabModelIndexToCommandId(int tab_model_index) { 92 // as the base command id.
87 int command_id = tab_model_index + kFirstTabCommandId; 93 int TabModelIndexToCommandId(int tab_model_index, int first_command_id) {
88 DCHECK_LT(command_id, kFirstWindowCommandId); 94 int command_id = tab_model_index + first_command_id;
95 DCHECK(first_command_id == kFirstForeignTabCommandId ||
96 command_id < kFirstLocalWindowCommandId);
89 return command_id; 97 return command_id;
90 } 98 }
91 99
92 // Convert |command_id| of menu item to index in tab model.
93 int CommandIdToTabModelIndex(int command_id) {
94 DCHECK_GE(command_id, kFirstTabCommandId);
95 DCHECK_LT(command_id, kFirstWindowCommandId);
96 return command_id - kFirstTabCommandId;
97 }
98
99 // Convert |window_model_index| to command id of menu item. 100 // Convert |window_model_index| to command id of menu item.
100 int WindowModelIndexToCommandId(int window_model_index) { 101 int WindowModelIndexToCommandId(int window_model_index) {
101 int command_id = window_model_index + kFirstWindowCommandId; 102 int command_id = window_model_index + kFirstLocalWindowCommandId;
103 DCHECK_LT(command_id, kFirstForeignTabCommandId);
102 DCHECK_LT(command_id, RecentTabsSubMenuModel::kRecentlyClosedHeaderCommandId); 104 DCHECK_LT(command_id, RecentTabsSubMenuModel::kRecentlyClosedHeaderCommandId);
103 return command_id; 105 return command_id;
104 } 106 }
105 107
106 // Convert |command_id| of menu item to index in window model. 108 // Convert |command_id| of menu item to index in window model.
107 int CommandIdToWindowModelIndex(int command_id) { 109 int CommandIdToWindowModelIndex(int command_id) {
108 DCHECK_GE(command_id, kFirstWindowCommandId); 110 DCHECK_GE(command_id, kFirstLocalWindowCommandId);
111 DCHECK_LT(command_id, kFirstForeignTabCommandId);
109 DCHECK_LT(command_id, RecentTabsSubMenuModel::kRecentlyClosedHeaderCommandId); 112 DCHECK_LT(command_id, RecentTabsSubMenuModel::kRecentlyClosedHeaderCommandId);
110 return command_id - kFirstWindowCommandId; 113 return command_id - kFirstLocalWindowCommandId;
111 } 114 }
112 115
113 } // namespace 116 } // namespace
114 117
115 enum RecentTabAction { 118 enum RecentTabAction {
116 LOCAL_SESSION_TAB = 0, 119 LOCAL_SESSION_TAB = 0,
117 OTHER_DEVICE_TAB, 120 OTHER_DEVICE_TAB,
118 RESTORE_WINDOW, 121 RESTORE_WINDOW,
119 SHOW_MORE, 122 SHOW_MORE,
120 LIMIT_RECENT_TAB_ACTION 123 LIMIT_RECENT_TAB_ACTION
121 }; 124 };
122 125
123 // An element in |RecentTabsSubMenuModel::tab_navigation_items_| that stores 126 // An element in |RecentTabsSubMenuModel::local_tab_navigation_items_| or
127 // |RecentTabsSubMenuModel::foreign_tab_navigation_items_| that stores
124 // the navigation information of a local or foreign tab required to restore the 128 // the navigation information of a local or foreign tab required to restore the
125 // tab. 129 // tab.
126 struct RecentTabsSubMenuModel::TabNavigationItem { 130 struct RecentTabsSubMenuModel::TabNavigationItem {
127 TabNavigationItem() : tab_id(-1) {} 131 TabNavigationItem() : tab_id(-1) {}
128 132
129 TabNavigationItem(const std::string& session_tag, 133 TabNavigationItem(const std::string& session_tag,
130 const SessionID::id_type& tab_id, 134 const SessionID::id_type& tab_id,
131 const string16& title, 135 const string16& title,
132 const GURL& url) 136 const GURL& url)
133 : session_tag(session_tag), 137 : session_tag(session_tag),
(...skipping 16 matching lines...) Expand all
150 const int RecentTabsSubMenuModel::kDisabledRecentlyClosedHeaderCommandId = 501; 154 const int RecentTabsSubMenuModel::kDisabledRecentlyClosedHeaderCommandId = 501;
151 const int RecentTabsSubMenuModel::kDeviceNameCommandId = 1000; 155 const int RecentTabsSubMenuModel::kDeviceNameCommandId = 1000;
152 156
153 RecentTabsSubMenuModel::RecentTabsSubMenuModel( 157 RecentTabsSubMenuModel::RecentTabsSubMenuModel(
154 ui::AcceleratorProvider* accelerator_provider, 158 ui::AcceleratorProvider* accelerator_provider,
155 Browser* browser, 159 Browser* browser,
156 browser_sync::SessionModelAssociator* associator) 160 browser_sync::SessionModelAssociator* associator)
157 : ui::SimpleMenuModel(this), 161 : ui::SimpleMenuModel(this),
158 browser_(browser), 162 browser_(browser),
159 associator_(associator), 163 associator_(associator),
164 last_local_model_index_(-1),
160 default_favicon_(ResourceBundle::GetSharedInstance(). 165 default_favicon_(ResourceBundle::GetSharedInstance().
161 GetNativeImageNamed(IDR_DEFAULT_FAVICON)), 166 GetNativeImageNamed(IDR_DEFAULT_FAVICON)),
162 weak_ptr_factory_(this) { 167 weak_ptr_factory_(this) {
168 // Invoke asynchronous call to load tabs from local last session, which does
169 // nothing if the tabs have already been loaded or they shouldn't be loaded.
170 // TabRestoreServiceChanged() will be called after the tabs are loaded.
171 TabRestoreService* service =
172 TabRestoreServiceFactory::GetForProfile(browser_->profile());
173 if (service) {
174 service->LoadTabsFromLastSession();
175
176 // TODO(sail): enable this when mac implements the dynamic menu, together with
177 // MenuModelDelegate::PrepareChange, OnItemRemoved, OnItemAdded and ChangesDone.
178 #if !defined(OS_MACOSX)
179 service->AddObserver(this);
180 #endif
181 }
182
183 // Build the menu.
163 Build(); 184 Build();
164 185
165 // Retrieve accelerator key for IDC_RESTORE_TAB now, because on ASH, it's not 186 // Retrieve accelerator key for IDC_RESTORE_TAB now, because on ASH, it's not
166 // defined in |accelerator_provider|, but in shell, so simply retrieve it now 187 // defined in |accelerator_provider|, but in shell, so simply retrieve it now
167 // for all ASH and non-ASH for use in |GetAcceleratorForCommandId|. 188 // for all ASH and non-ASH for use in |GetAcceleratorForCommandId|.
168 #if defined(USE_ASH) 189 #if defined(USE_ASH)
169 for (size_t i = 0; i < ash::kAcceleratorDataLength; ++i) { 190 for (size_t i = 0; i < ash::kAcceleratorDataLength; ++i) {
170 const ash::AcceleratorData& accel_data = ash::kAcceleratorData[i]; 191 const ash::AcceleratorData& accel_data = ash::kAcceleratorData[i];
171 if (accel_data.action == ash::RESTORE_TAB) { 192 if (accel_data.action == ash::RESTORE_TAB) {
172 reopen_closed_tab_accelerator_ = ui::Accelerator(accel_data.keycode, 193 reopen_closed_tab_accelerator_ = ui::Accelerator(accel_data.keycode,
173 accel_data.modifiers); 194 accel_data.modifiers);
174 break; 195 break;
175 } 196 }
176 } 197 }
177 #else 198 #else
178 if (accelerator_provider) { 199 if (accelerator_provider) {
179 accelerator_provider->GetAcceleratorForCommandId( 200 accelerator_provider->GetAcceleratorForCommandId(
180 IDC_RESTORE_TAB, &reopen_closed_tab_accelerator_); 201 IDC_RESTORE_TAB, &reopen_closed_tab_accelerator_);
181 } 202 }
182 #endif // defined(USE_ASH) 203 #endif // defined(USE_ASH)
183 } 204 }
184 205
185 RecentTabsSubMenuModel::~RecentTabsSubMenuModel() { 206 RecentTabsSubMenuModel::~RecentTabsSubMenuModel() {
207 TabRestoreService* service =
208 TabRestoreServiceFactory::GetForProfile(browser_->profile());
209 if (service)
210 service->RemoveObserver(this);
211 }
212
213 int RecentTabsSubMenuModel::ModelIndexToIdInParentMenu(int model_index) const {
214 // If |model_index| identifies a local tab/window menu item, return as is;
215 // otherwise, reserve enough id's for maximum possible local entries.
216 if (model_index <= last_local_model_index_)
217 return model_index;
218 return model_index + kMaxLocalEntries - last_local_model_index_;
219 }
220
221 int RecentTabsSubMenuModel::IdInParentMenuToModelIndex(
222 int id_in_parent_menu) const {
223 // If |id_in_parent_menu| identifies a local tab/window menu item, return as
224 // is; otherwise, ModelIndexToIdInParentMenu() would have reserved enough Id's
225 // for maximum possible local entries, so reverse that logic.
226 int index = id_in_parent_menu <= last_local_model_index_ ? id_in_parent_menu :
227 id_in_parent_menu - kMaxLocalEntries + last_local_model_index_;
228 DCHECK(index >= 0 && index < GetItemCount());
229 return index;
186 } 230 }
187 231
188 bool RecentTabsSubMenuModel::IsCommandIdChecked(int command_id) const { 232 bool RecentTabsSubMenuModel::IsCommandIdChecked(int command_id) const {
189 return false; 233 return false;
190 } 234 }
191 235
192 bool RecentTabsSubMenuModel::IsCommandIdEnabled(int command_id) const { 236 bool RecentTabsSubMenuModel::IsCommandIdEnabled(int command_id) const {
193 if (command_id == kRecentlyClosedHeaderCommandId || 237 if (command_id == kRecentlyClosedHeaderCommandId ||
194 command_id == kDisabledRecentlyClosedHeaderCommandId || 238 command_id == kDisabledRecentlyClosedHeaderCommandId ||
195 command_id == kDeviceNameCommandId || 239 command_id == kDeviceNameCommandId ||
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
231 ui::DispositionFromEventFlags(event_flags); 275 ui::DispositionFromEventFlags(event_flags);
232 if (disposition == CURRENT_TAB) // Force to open a new foreground tab. 276 if (disposition == CURRENT_TAB) // Force to open a new foreground tab.
233 disposition = NEW_FOREGROUND_TAB; 277 disposition = NEW_FOREGROUND_TAB;
234 278
235 TabRestoreService* service = 279 TabRestoreService* service =
236 TabRestoreServiceFactory::GetForProfile(browser_->profile()); 280 TabRestoreServiceFactory::GetForProfile(browser_->profile());
237 TabRestoreServiceDelegate* delegate = 281 TabRestoreServiceDelegate* delegate =
238 TabRestoreServiceDelegate::FindDelegateForWebContents( 282 TabRestoreServiceDelegate::FindDelegateForWebContents(
239 browser_->tab_strip_model()->GetActiveWebContents()); 283 browser_->tab_strip_model()->GetActiveWebContents());
240 if (IsTabModelCommandId(command_id)) { 284 if (IsTabModelCommandId(command_id)) {
241 int model_idx = CommandIdToTabModelIndex(command_id); 285 TabNavigationItems* tab_items = NULL;
242 DCHECK(model_idx >= 0 && 286 int model_idx = CommandIdToTabModelIndex(command_id, &tab_items);
243 model_idx < static_cast<int>(tab_navigation_items_.size())); 287 const TabNavigationItem& item = tab_items->at(model_idx);
244 const TabNavigationItem& item = tab_navigation_items_[model_idx];
245 DCHECK(item.tab_id > -1 && item.url.is_valid()); 288 DCHECK(item.tab_id > -1 && item.url.is_valid());
246 289
247 if (item.session_tag.empty()) { // Restore tab of local session. 290 if (item.session_tag.empty()) { // Restore tab of local session.
248 if (service && delegate) { 291 if (service && delegate) {
249 UMA_HISTOGRAM_ENUMERATION("WrenchMenu.RecentTabsSubMenu", 292 UMA_HISTOGRAM_ENUMERATION("WrenchMenu.RecentTabsSubMenu",
250 LOCAL_SESSION_TAB, LIMIT_RECENT_TAB_ACTION); 293 LOCAL_SESSION_TAB, LIMIT_RECENT_TAB_ACTION);
251 service->RestoreEntryById(delegate, item.tab_id, 294 service->RestoreEntryById(delegate, item.tab_id,
252 browser_->host_desktop_type(), disposition); 295 browser_->host_desktop_type(), disposition);
253 } 296 }
254 } else { // Restore tab of foreign session. 297 } else { // Restore tab of foreign session.
255 browser_sync::SessionModelAssociator* associator = GetModelAssociator(); 298 browser_sync::SessionModelAssociator* associator = GetModelAssociator();
256 if (!associator) 299 if (!associator)
257 return; 300 return;
258 const SessionTab* tab; 301 const SessionTab* tab;
259 if (!associator->GetForeignTab(item.session_tag, item.tab_id, &tab)) 302 if (!associator->GetForeignTab(item.session_tag, item.tab_id, &tab))
260 return; 303 return;
261 if (tab->navigations.empty()) 304 if (tab->navigations.empty())
262 return; 305 return;
263 UMA_HISTOGRAM_ENUMERATION("WrenchMenu.RecentTabsSubMenu", 306 UMA_HISTOGRAM_ENUMERATION("WrenchMenu.RecentTabsSubMenu",
264 OTHER_DEVICE_TAB, LIMIT_RECENT_TAB_ACTION); 307 OTHER_DEVICE_TAB, LIMIT_RECENT_TAB_ACTION);
265 SessionRestore::RestoreForeignSessionTab( 308 SessionRestore::RestoreForeignSessionTab(
266 browser_->tab_strip_model()->GetActiveWebContents(), 309 browser_->tab_strip_model()->GetActiveWebContents(),
267 *tab, disposition); 310 *tab, disposition);
268 } 311 }
269 } else { 312 } else {
270 DCHECK(IsWindowModelCommandId(command_id)); 313 DCHECK(IsWindowModelCommandId(command_id));
271 if (service && delegate) { 314 if (service && delegate) {
272 int model_idx = CommandIdToWindowModelIndex(command_id); 315 int model_idx = CommandIdToWindowModelIndex(command_id);
273 DCHECK(model_idx >= 0 && 316 DCHECK(model_idx >= 0 &&
274 model_idx < static_cast<int>(window_items_.size())); 317 model_idx < static_cast<int>(local_window_items_.size()));
275 UMA_HISTOGRAM_ENUMERATION("WrenchMenu.RecentTabsSubMenu", RESTORE_WINDOW, 318 UMA_HISTOGRAM_ENUMERATION("WrenchMenu.RecentTabsSubMenu", RESTORE_WINDOW,
276 LIMIT_RECENT_TAB_ACTION); 319 LIMIT_RECENT_TAB_ACTION);
277 service->RestoreEntryById(delegate, window_items_[model_idx], 320 service->RestoreEntryById(delegate, local_window_items_[model_idx],
278 browser_->host_desktop_type(), disposition); 321 browser_->host_desktop_type(), disposition);
279 } 322 }
280 } 323 }
281 } 324 }
282 325
283 const gfx::Font* RecentTabsSubMenuModel::GetLabelFontAt(int index) const { 326 const gfx::Font* RecentTabsSubMenuModel::GetLabelFontAt(int index) const {
284 int command_id = GetCommandIdAt(index); 327 int command_id = GetCommandIdAt(index);
285 if (command_id == kDeviceNameCommandId || 328 if (command_id == kDeviceNameCommandId ||
286 command_id == kRecentlyClosedHeaderCommandId) { 329 command_id == kRecentlyClosedHeaderCommandId) {
287 return &ResourceBundle::GetSharedInstance().GetFont( 330 return &ResourceBundle::GetSharedInstance().GetFont(
288 ResourceBundle::BoldFont); 331 ResourceBundle::BoldFont);
289 } 332 }
290 return NULL; 333 return NULL;
291 } 334 }
292 335
293 int RecentTabsSubMenuModel::GetMaxWidthForItemAtIndex(int item_index) const { 336 int RecentTabsSubMenuModel::GetMaxWidthForItemAtIndex(int item_index) const {
294 int command_id = GetCommandIdAt(item_index); 337 int command_id = GetCommandIdAt(item_index);
295 if (command_id == IDC_RECENT_TABS_NO_DEVICE_TABS || 338 if (command_id == IDC_RECENT_TABS_NO_DEVICE_TABS ||
296 command_id == kRecentlyClosedHeaderCommandId || 339 command_id == kRecentlyClosedHeaderCommandId ||
297 command_id == kDisabledRecentlyClosedHeaderCommandId) { 340 command_id == kDisabledRecentlyClosedHeaderCommandId) {
298 return -1; 341 return -1;
299 } 342 }
300 return 320; 343 return 320;
301 } 344 }
302 345
303 bool RecentTabsSubMenuModel::GetURLAndTitleForItemAtIndex( 346 bool RecentTabsSubMenuModel::GetURLAndTitleForItemAtIndex(int index,
304 int index, 347 std::string* url,
305 std::string* url, 348 string16* title) {
306 string16* title) const {
307 int command_id = GetCommandIdAt(index); 349 int command_id = GetCommandIdAt(index);
308 if (IsTabModelCommandId(command_id)) { 350 if (IsTabModelCommandId(command_id)) {
309 int model_idx = CommandIdToTabModelIndex(command_id); 351 TabNavigationItems* tab_items = NULL;
310 DCHECK(model_idx >= 0 && 352 int model_idx = CommandIdToTabModelIndex(command_id, &tab_items);
311 model_idx < static_cast<int>(tab_navigation_items_.size())); 353 *url = tab_items->at(model_idx).url.possibly_invalid_spec();
312 *url = tab_navigation_items_[model_idx].url.possibly_invalid_spec(); 354 *title = tab_items->at(model_idx).title;
313 *title = tab_navigation_items_[model_idx].title;
314 return true; 355 return true;
315 } 356 }
316 return false; 357 return false;
317 } 358 }
318 359
319 void RecentTabsSubMenuModel::Build() { 360 void RecentTabsSubMenuModel::Build() {
320 // The menu contains: 361 // The menu contains:
321 // - Recently closed tabs header, then list of tabs, then separator 362 // - Recently closed header, then list of local recently closed tabs/windows,
363 // then separator
322 // - device 1 section header, then list of tabs from device, then separator 364 // - device 1 section header, then list of tabs from device, then separator
323 // - device 2 section header, then list of tabs from device, then separator 365 // - device 2 section header, then list of tabs from device, then separator
324 // - device 3 section header, then list of tabs from device, then separator 366 // - device 3 section header, then list of tabs from device, then separator
325 // - More... to open the history tab to get more other devices. 367 // - More... to open the history tab to get more other devices.
326 // |tab_navigation_items_| only contains navigatable (and hence executable) 368 // |local_tab_navigation_items_| and |foreign_tab_navigation_items_| only
327 // tab items for other devices, and |window_items_| contains the recently 369 // contain navigatable (and hence executable) tab items for local recently
328 // closed windows. 370 // closed tabs and tabs of other devices respectively.
329 BuildRecentTabs(); 371 // |local_window_items_| contains the local recently closed windows.
330 BuildDevices(); 372 BuildLocalEntries();
373 BuildForeignTabs();
331 } 374 }
332 375
333 void RecentTabsSubMenuModel::BuildRecentTabs() { 376 void RecentTabsSubMenuModel::BuildLocalEntries() {
334 ListValue recently_closed_list; 377 // All local items use InsertItem*At() to append or insert a menu item.
378 // We're appending if building the entries for the first time i.e. invoked
379 // from Constructor(), inserting when local entries change subsequently i.e.
380 // invoked from TabRestoreServiceChanged().
381
382 int curr_model_index = 0;
383
335 TabRestoreService* service = 384 TabRestoreService* service =
336 TabRestoreServiceFactory::GetForProfile(browser_->profile()); 385 TabRestoreServiceFactory::GetForProfile(browser_->profile());
337 if (service) {
338 // This does nothing if the tabs have already been loaded or they
339 // shouldn't be loaded.
340 service->LoadTabsFromLastSession();
341 }
342
343 if (!service || service->entries().size() == 0) { 386 if (!service || service->entries().size() == 0) {
344 // This is to show a disabled restore tab entry with the accelerator to 387 // This is to show a disabled restore tab entry with the accelerator to
345 // teach users about this command. 388 // teach users about this command.
346 AddItemWithStringId(kDisabledRecentlyClosedHeaderCommandId, 389 InsertItemWithStringIdAt(curr_model_index++,
347 IDS_NEW_TAB_RECENTLY_CLOSED); 390 kDisabledRecentlyClosedHeaderCommandId,
348 return; 391 IDS_NEW_TAB_RECENTLY_CLOSED);
392 } else {
393 InsertItemWithStringIdAt(curr_model_index,
394 kRecentlyClosedHeaderCommandId,
395 IDS_NEW_TAB_RECENTLY_CLOSED);
396 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
397 SetIcon(curr_model_index,
398 rb.GetNativeImageNamed(IDR_RECENTLY_CLOSED_WINDOW));
399 ++curr_model_index;
400
401 int added_count = 0;
402 TabRestoreService::Entries entries = service->entries();
403 for (TabRestoreService::Entries::const_iterator it = entries.begin();
404 it != entries.end() && added_count < kMaxLocalEntries; ++it) {
405 TabRestoreService::Entry* entry = *it;
406 if (entry->type == TabRestoreService::TAB) {
407 TabRestoreService::Tab* tab =
408 static_cast<TabRestoreService::Tab*>(entry);
409 const sessions::SerializedNavigationEntry& current_navigation =
410 tab->navigations.at(tab->current_navigation_index);
411 BuildLocalTabItem(
412 entry->id,
413 current_navigation.title(),
414 current_navigation.virtual_url(),
415 curr_model_index);
416 } else {
417 DCHECK_EQ(entry->type, TabRestoreService::WINDOW);
418 BuildLocalWindowItem(
419 entry->id,
420 static_cast<TabRestoreService::Window*>(entry)->tabs.size(),
421 curr_model_index);
422 }
423 ++added_count;
424 ++curr_model_index;
425 }
349 } 426 }
350 427
351 AddItemWithStringId(kRecentlyClosedHeaderCommandId, 428 DCHECK_GT(curr_model_index, 0);
352 IDS_NEW_TAB_RECENTLY_CLOSED); 429 last_local_model_index_ = curr_model_index - 1;
353 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
354 SetIcon(GetItemCount() - 1,
355 rb.GetNativeImageNamed(IDR_RECENTLY_CLOSED_WINDOW));
356
357 int added_count = 0;
358 TabRestoreService::Entries entries = service->entries();
359 for (TabRestoreService::Entries::const_iterator it = entries.begin();
360 it != entries.end() && added_count < kMaxRecentlyClosedEntries; ++it) {
361 TabRestoreService::Entry* entry = *it;
362 if (entry->type == TabRestoreService::TAB) {
363 TabRestoreService::Tab* tab = static_cast<TabRestoreService::Tab*>(entry);
364 const sessions::SerializedNavigationEntry& current_navigation =
365 tab->navigations.at(tab->current_navigation_index);
366 BuildLocalTabItem(
367 entry->id,
368 current_navigation.title(),
369 current_navigation.virtual_url());
370 } else {
371 DCHECK_EQ(entry->type, TabRestoreService::WINDOW);
372 BuildWindowItem(
373 entry->id,
374 static_cast<TabRestoreService::Window*>(entry)->tabs.size());
375 }
376 ++added_count;
377 }
378 } 430 }
379 431
380 void RecentTabsSubMenuModel::BuildDevices() { 432 void RecentTabsSubMenuModel::BuildForeignTabs() {
433 // All foreign items (device headers or tabs) use AddItem*() to append a menu
434 // item, because they are always only built once (i.e. invoked from
435 // Constructor()) and don't change after that.
436
381 browser_sync::SessionModelAssociator* associator = GetModelAssociator(); 437 browser_sync::SessionModelAssociator* associator = GetModelAssociator();
382 std::vector<const browser_sync::SyncedSession*> sessions; 438 std::vector<const browser_sync::SyncedSession*> sessions;
383 if (!associator || !associator->GetAllForeignSessions(&sessions)) { 439 if (!associator || !associator->GetAllForeignSessions(&sessions)) {
384 AddSeparator(ui::NORMAL_SEPARATOR); 440 AddSeparator(ui::NORMAL_SEPARATOR);
385 AddItemWithStringId(IDC_RECENT_TABS_NO_DEVICE_TABS, 441 AddItemWithStringId(IDC_RECENT_TABS_NO_DEVICE_TABS,
386 IDS_RECENT_TABS_NO_DEVICE_TABS); 442 IDS_RECENT_TABS_NO_DEVICE_TABS);
387 return; 443 return;
388 } 444 }
389 445
390 // Sort sessions from most recent to least recent. 446 // Sort sessions from most recent to least recent.
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
444 500
445 ++num_sessions_added; 501 ++num_sessions_added;
446 } // for all sessions 502 } // for all sessions
447 503
448 // We are not supposed to get here unless at least some items were added. 504 // We are not supposed to get here unless at least some items were added.
449 DCHECK_GT(GetItemCount(), 0); 505 DCHECK_GT(GetItemCount(), 0);
450 AddSeparator(ui::NORMAL_SEPARATOR); 506 AddSeparator(ui::NORMAL_SEPARATOR);
451 AddItemWithStringId(IDC_SHOW_HISTORY, IDS_RECENT_TABS_MORE); 507 AddItemWithStringId(IDC_SHOW_HISTORY, IDS_RECENT_TABS_MORE);
452 } 508 }
453 509
454 void RecentTabsSubMenuModel::BuildLocalTabItem( 510 void RecentTabsSubMenuModel::BuildLocalTabItem(int session_id,
455 int session_id, 511 const string16& title,
456 const string16& title, 512 const GURL& url,
457 const GURL& url) { 513 int curr_model_index) {
458 TabNavigationItem item("", session_id, title, url); 514 TabNavigationItem item("", session_id, title, url);
459 int command_id = TabModelIndexToCommandId(tab_navigation_items_.size()); 515 int command_id = TabModelIndexToCommandId(
516 local_tab_navigation_items_.size(), kFirstLocalTabCommandId);
517 // See comments in BuildLocalEntries() about usage of InsertItem*At().
460 // There may be no tab title, in which case, use the url as tab title. 518 // There may be no tab title, in which case, use the url as tab title.
461 AddItem(command_id, title.empty() ? UTF8ToUTF16(item.url.spec()) : title); 519 InsertItemAt(curr_model_index, command_id,
462 AddTabFavicon(tab_navigation_items_.size(), command_id, item.url); 520 title.empty() ? UTF8ToUTF16(item.url.spec()) : title);
463 tab_navigation_items_.push_back(item); 521 AddTabFavicon(command_id, item.url);
522 local_tab_navigation_items_.push_back(item);
464 } 523 }
465 524
466 void RecentTabsSubMenuModel::BuildForeignTabItem( 525 void RecentTabsSubMenuModel::BuildLocalWindowItem(
467 const std::string& session_tag, 526 const SessionID::id_type& window_id,
468 const SessionTab& tab) { 527 int num_tabs,
528 int curr_model_index) {
529 int command_id = WindowModelIndexToCommandId(local_window_items_.size());
530 // See comments in BuildLocalEntries() about usage of InsertItem*At().
531 if (num_tabs == 1) {
532 InsertItemWithStringIdAt(curr_model_index, command_id,
533 IDS_NEW_TAB_RECENTLY_CLOSED_WINDOW_SINGLE);
534 } else {
535 InsertItemAt(curr_model_index, command_id, l10n_util::GetStringFUTF16(
536 IDS_NEW_TAB_RECENTLY_CLOSED_WINDOW_MULTIPLE,
537 base::IntToString16(num_tabs)));
538 }
539 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
540 SetIcon(curr_model_index, rb.GetNativeImageNamed(IDR_RECENTLY_CLOSED_WINDOW));
541 local_window_items_.push_back(window_id);
542 }
543
544 void RecentTabsSubMenuModel::BuildForeignTabItem(const std::string& session_tag,
545 const SessionTab& tab) {
469 const sessions::SerializedNavigationEntry& current_navigation = 546 const sessions::SerializedNavigationEntry& current_navigation =
470 tab.navigations.at(tab.normalized_navigation_index()); 547 tab.navigations.at(tab.normalized_navigation_index());
471 TabNavigationItem item(session_tag, tab.tab_id.id(), 548 TabNavigationItem item(session_tag, tab.tab_id.id(),
472 current_navigation.title(), 549 current_navigation.title(),
473 current_navigation.virtual_url()); 550 current_navigation.virtual_url());
474 int command_id = TabModelIndexToCommandId(tab_navigation_items_.size()); 551 int command_id = TabModelIndexToCommandId(
552 foreign_tab_navigation_items_.size(), kFirstForeignTabCommandId);
553 // See comments in BuildForeignTabs() about usage of AddItem*().
475 // There may be no tab title, in which case, use the url as tab title. 554 // There may be no tab title, in which case, use the url as tab title.
476 AddItem(command_id, 555 AddItem(command_id,
477 current_navigation.title().empty() ? 556 current_navigation.title().empty() ?
478 UTF8ToUTF16(item.url.spec()) : current_navigation.title()); 557 UTF8ToUTF16(item.url.spec()) : current_navigation.title());
479 AddTabFavicon(tab_navigation_items_.size(), command_id, item.url); 558 AddTabFavicon(command_id, item.url);
480 tab_navigation_items_.push_back(item); 559 foreign_tab_navigation_items_.push_back(item);
481 }
482
483 void RecentTabsSubMenuModel::BuildWindowItem(
484 const SessionID::id_type& window_id,
485 int num_tabs) {
486 int command_id = WindowModelIndexToCommandId(window_items_.size());
487 if (num_tabs == 1) {
488 AddItemWithStringId(command_id, IDS_NEW_TAB_RECENTLY_CLOSED_WINDOW_SINGLE);
489 } else {
490 AddItem(command_id, l10n_util::GetStringFUTF16(
491 IDS_NEW_TAB_RECENTLY_CLOSED_WINDOW_MULTIPLE,
492 base::IntToString16(num_tabs)));
493 }
494 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
495 SetIcon(GetItemCount() - 1,
496 rb.GetNativeImageNamed(IDR_RECENTLY_CLOSED_WINDOW));
497 window_items_.push_back(window_id);
498 } 560 }
499 561
500 void RecentTabsSubMenuModel::AddDeviceFavicon( 562 void RecentTabsSubMenuModel::AddDeviceFavicon(
501 int index_in_menu, 563 int index_in_menu,
502 browser_sync::SyncedSession::DeviceType device_type) { 564 browser_sync::SyncedSession::DeviceType device_type) {
503 int favicon_id = -1; 565 int favicon_id = -1;
504 switch (device_type) { 566 switch (device_type) {
505 case browser_sync::SyncedSession::TYPE_PHONE: 567 case browser_sync::SyncedSession::TYPE_PHONE:
506 favicon_id = IDR_PHONE_FAVICON; 568 favicon_id = IDR_PHONE_FAVICON;
507 break; 569 break;
508 570
509 case browser_sync::SyncedSession::TYPE_TABLET: 571 case browser_sync::SyncedSession::TYPE_TABLET:
510 favicon_id = IDR_TABLET_FAVICON; 572 favicon_id = IDR_TABLET_FAVICON;
511 break; 573 break;
512 574
513 case browser_sync::SyncedSession::TYPE_CHROMEOS: 575 case browser_sync::SyncedSession::TYPE_CHROMEOS:
514 case browser_sync::SyncedSession::TYPE_WIN: 576 case browser_sync::SyncedSession::TYPE_WIN:
515 case browser_sync::SyncedSession::TYPE_MACOSX: 577 case browser_sync::SyncedSession::TYPE_MACOSX:
516 case browser_sync::SyncedSession::TYPE_LINUX: 578 case browser_sync::SyncedSession::TYPE_LINUX:
517 case browser_sync::SyncedSession::TYPE_OTHER: 579 case browser_sync::SyncedSession::TYPE_OTHER:
518 case browser_sync::SyncedSession::TYPE_UNSET: 580 case browser_sync::SyncedSession::TYPE_UNSET:
519 favicon_id = IDR_LAPTOP_FAVICON; 581 favicon_id = IDR_LAPTOP_FAVICON;
520 break; 582 break;
521 }; 583 };
522 584
523 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 585 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
524 SetIcon(index_in_menu, rb.GetNativeImageNamed(favicon_id)); 586 SetIcon(index_in_menu, rb.GetNativeImageNamed(favicon_id));
525 } 587 }
526 588
527 void RecentTabsSubMenuModel::AddTabFavicon(int model_index, 589 void RecentTabsSubMenuModel::AddTabFavicon(int command_id, const GURL& url) {
528 int command_id,
529 const GURL& url) {
530 int index_in_menu = GetIndexOfCommandId(command_id); 590 int index_in_menu = GetIndexOfCommandId(command_id);
531 591
532 // If tab has synced favicon, use it. 592 // If tab has synced favicon, use it.
533 // Note that currently, foreign tab only has favicon if --sync-tab-favicons 593 // Note that currently, foreign tab only has favicon if --sync-tab-favicons
534 // switch is on; according to zea@, this flag is now automatically enabled for 594 // switch is on; according to zea@, this flag is now automatically enabled for
535 // iOS and android, and they're looking into enabling it for other platforms. 595 // iOS and android, and they're looking into enabling it for other platforms.
536 browser_sync::SessionModelAssociator* associator = GetModelAssociator(); 596 browser_sync::SessionModelAssociator* associator = GetModelAssociator();
537 scoped_refptr<base::RefCountedMemory> favicon_png; 597 scoped_refptr<base::RefCountedMemory> favicon_png;
538 if (associator && 598 if (associator &&
539 associator->GetSyncedFaviconForPageURL(url.spec(), &favicon_png)) { 599 associator->GetSyncedFaviconForPageURL(url.spec(), &favicon_png)) {
540 gfx::Image image = gfx::Image::CreateFrom1xPNGBytes( 600 gfx::Image image = gfx::Image::CreateFrom1xPNGBytes(favicon_png->front(),
541 favicon_png->front(), 601 favicon_png->size());
542 favicon_png->size());
543 SetIcon(index_in_menu, image); 602 SetIcon(index_in_menu, image);
544 return; 603 return;
545 } 604 }
546 605
547 // Otherwise, start to fetch the favicon from local history asynchronously. 606 // Otherwise, start to fetch the favicon from local history asynchronously.
548 // Set default icon first. 607 // Set default icon first.
549 SetIcon(index_in_menu, default_favicon_); 608 SetIcon(index_in_menu, default_favicon_);
550 // Start request to fetch actual icon if possible. 609 // Start request to fetch actual icon if possible.
551 FaviconService* favicon_service = FaviconServiceFactory::GetForProfile( 610 FaviconService* favicon_service = FaviconServiceFactory::GetForProfile(
552 browser_->profile(), Profile::EXPLICIT_ACCESS); 611 browser_->profile(), Profile::EXPLICIT_ACCESS);
553 if (!favicon_service) 612 if (!favicon_service)
554 return; 613 return;
555 614
556 favicon_service->GetFaviconImageForURL( 615 favicon_service->GetFaviconImageForURL(
557 FaviconService::FaviconForURLParams(browser_->profile(), 616 FaviconService::FaviconForURLParams(browser_->profile(),
558 url, 617 url,
559 chrome::FAVICON, 618 chrome::FAVICON,
560 gfx::kFaviconSize), 619 gfx::kFaviconSize),
561 base::Bind(&RecentTabsSubMenuModel::OnFaviconDataAvailable, 620 base::Bind(&RecentTabsSubMenuModel::OnFaviconDataAvailable,
562 weak_ptr_factory_.GetWeakPtr(), 621 weak_ptr_factory_.GetWeakPtr(),
563 command_id), 622 command_id),
564 &cancelable_task_tracker_); 623 command_id >= kFirstForeignTabCommandId ?
624 &foreign_tab_cancelable_task_tracker_ :
625 &local_tab_cancelable_task_tracker_);
565 } 626 }
566 627
567 void RecentTabsSubMenuModel::OnFaviconDataAvailable( 628 void RecentTabsSubMenuModel::OnFaviconDataAvailable(
568 int command_id, 629 int command_id,
569 const chrome::FaviconImageResult& image_result) { 630 const chrome::FaviconImageResult& image_result) {
570 if (image_result.image.IsEmpty()) 631 if (image_result.image.IsEmpty())
571 return; 632 return;
572 DCHECK(!tab_navigation_items_.empty());
573 int index_in_menu = GetIndexOfCommandId(command_id); 633 int index_in_menu = GetIndexOfCommandId(command_id);
574 DCHECK(index_in_menu != -1); 634 DCHECK_GT(index_in_menu, -1);
575 SetIcon(index_in_menu, image_result.image); 635 SetIcon(index_in_menu, image_result.image);
576 if (GetMenuModelDelegate()) 636 if (GetMenuModelDelegate())
577 GetMenuModelDelegate()->OnIconChanged(index_in_menu); 637 GetMenuModelDelegate()->OnIconChanged(index_in_menu);
578 } 638 }
579 639
640 int RecentTabsSubMenuModel::CommandIdToTabModelIndex(
641 int command_id, TabNavigationItems** tab_items) {
642 if (command_id >= kFirstForeignTabCommandId) {
643 *tab_items = &foreign_tab_navigation_items_;
644 return command_id - kFirstForeignTabCommandId;
645 }
646 DCHECK_GE(command_id, kFirstLocalTabCommandId);
647 DCHECK_LT(command_id, kFirstLocalWindowCommandId);
648 *tab_items = &local_tab_navigation_items_;
649 return command_id - kFirstLocalTabCommandId;
650 }
651
652 void RecentTabsSubMenuModel::ClearLocalEntries() {
653 // Remove local items (recent tabs and windows) from model.
654 while (last_local_model_index_ >= 0)
655 RemoveItemAt(last_local_model_index_--);
656
657 // Cancel asynchronous FaviconService::GetFaviconImageForURL() tasks of all
658 // local tabs.
659 local_tab_cancelable_task_tracker_.TryCancelAll();
660
661 // Remove all local tab navigation items.
662 local_tab_navigation_items_.clear();
663
664 // Remove all local window items.
665 local_window_items_.clear();
666 }
667
580 browser_sync::SessionModelAssociator* 668 browser_sync::SessionModelAssociator*
581 RecentTabsSubMenuModel::GetModelAssociator() { 669 RecentTabsSubMenuModel::GetModelAssociator() {
582 if (!associator_) { 670 if (!associator_) {
583 ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()-> 671 ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
584 GetForProfile(browser_->profile()); 672 GetForProfile(browser_->profile());
585 // Only return the associator if it exists and it is done syncing sessions. 673 // Only return the associator if it exists and it is done syncing sessions.
586 if (service && service->ShouldPushChanges()) 674 if (service && service->ShouldPushChanges())
587 associator_ = service->GetSessionModelAssociator(); 675 associator_ = service->GetSessionModelAssociator();
588 } 676 }
589 return associator_; 677 return associator_;
590 } 678 }
679
680 void RecentTabsSubMenuModel::TabRestoreServiceChanged(
681 TabRestoreService* service) {
682 ui::MenuModelDelegate* menu_model_delegate = GetMenuModelDelegate();
683 if (menu_model_delegate) {
684 menu_model_delegate->PrepareForChange();
685
686 // Notify delegate to remove all local entries from the menu; do so in
687 // decreasing order so that indexes of remaining items don't change.
688 for (int i = last_local_model_index_; i >=0; --i)
689 menu_model_delegate->OnItemRemoved(i);
690 }
691
692 ClearLocalEntries();
693
694 BuildLocalEntries();
695
696 if (menu_model_delegate) {
697 // Notify delegate to add the new local entries to the menu.
698 for (int i = 0; i <= last_local_model_index_; ++i)
699 menu_model_delegate->OnItemAdded(i);
700
701 menu_model_delegate->ChangesDone();
702 }
703 }
704
705 void RecentTabsSubMenuModel::TabRestoreServiceDestroyed(
706 TabRestoreService* service) {
707 TabRestoreServiceChanged(service);
708 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698