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

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

Issue 11298004: alternate ntp: add "Recent Tabs" submenu to wrench menu (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 1 month 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
(Empty)
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.
4
5 #include "chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.h"
6
7 #include "base/bind.h"
8 #include "base/string_number_conversions.h"
9 #include "base/utf_string_conversions.h"
10 #include "chrome/app/chrome_command_ids.h"
11 #include "chrome/browser/favicon/favicon_service_factory.h"
12 #include "chrome/browser/prefs/scoped_user_pref_update.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/sessions/session_restore.h"
15 #include "chrome/browser/sessions/tab_restore_service_delegate.h"
16 #include "chrome/browser/sessions/tab_restore_service_factory.h"
17 #include "chrome/browser/sync/glue/session_model_associator.h"
18 #include "chrome/browser/sync/glue/synced_session.h"
19 #include "chrome/browser/sync/profile_sync_service.h"
20 #include "chrome/browser/sync/profile_sync_service_factory.h"
21 #include "chrome/browser/ui/browser.h"
22 #include "chrome/browser/ui/browser_commands.h"
23 #include "chrome/browser/ui/browser_tabstrip.h"
24 #include "chrome/common/pref_names.h"
25 #include "chrome/common/time_format.h"
26 #include "chrome/common/url_constants.h"
27 #include "grit/generated_resources.h"
28 #include "grit/ui_resources.h"
29 #include "ui/base/accelerators/accelerator.h"
30 #include "ui/base/l10n/l10n_util.h"
31 #include "ui/base/resource/resource_bundle.h"
32 #include "ui/gfx/favicon_size.h"
33
34 namespace {
35
36 // Comparator function for use with std::sort that will sort sessions by
37 // descending modified_time (i.e., most recent first).
38 bool SortSessionsByRecency(const browser_sync::SyncedSession* s1,
39 const browser_sync::SyncedSession* s2) {
40 return s1->modified_time > s2->modified_time;
41 }
42
43 // Helper function that returns the largest tab timestamp for the window.
44 base::Time GetMostRecentTabTimestamp(const SessionWindow* window) {
45 base::Time max_timestamp;
46 for (size_t i = 0, num_tabs = window->tabs.size(); i < num_tabs; ++i) {
47 SessionTab* tab = window->tabs[i];
48 if (tab->timestamp > max_timestamp)
49 max_timestamp = tab->timestamp;
50 }
51 return max_timestamp;
52 }
53
54 // Comparator function to sort windows by last-modified time. Windows in a
55 // session share the same timestamp, so we need to use tab timestamps instead.
56 // instead.
57 bool SortWindowsByRecency(const SessionWindow* w1, const SessionWindow* w2) {
58 return GetMostRecentTabTimestamp(w1) > GetMostRecentTabTimestamp(w2);
59 }
60
61 } // namepace
62
63 struct RecentTabsSubMenuModel::NavigationItem {
sky 2012/11/07 00:10:47 Add description.
kuan 2012/11/07 02:29:46 Done.
64 NavigationItem() : tab_id(-1) {
65 }
66 NavigationItem(const std::string& session_tag,
sky 2012/11/07 00:10:47 nit: newline between 65/66 and 70/71.
kuan 2012/11/07 02:29:46 Done.
67 const SessionID::id_type& tab_id,
68 const GURL& url)
69 : session_tag(session_tag), tab_id(tab_id), url(url) {
sky 2012/11/07 00:10:47 since you wrapped the constructor wrap each param
kuan 2012/11/07 02:29:46 Done.
70 }
71 // For use by std::set for sorting.
72 bool operator<(const NavigationItem& other) const {
73 return url < other.url;
74 }
75
76 std::string session_tag;
77 SessionID::id_type tab_id;
78 GURL url;
79 };
80
81 RecentTabsSubMenuModel::RecentTabsSubMenuModel(
82 ui::AcceleratorProvider* accelerator_provider,
83 Browser* browser)
84 : ALLOW_THIS_IN_INITIALIZER_LIST(ui::SimpleMenuModel(this)),
85 accelerator_provider_(accelerator_provider),
86 browser_(browser),
87 default_favicon_(NULL),
88 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) {
89 Build();
90 }
91
92 RecentTabsSubMenuModel::~RecentTabsSubMenuModel() {
93 weak_ptr_factory_.InvalidateWeakPtrs();
94 }
95
96 bool RecentTabsSubMenuModel::IsCommandIdChecked(int command_id) const {
97 return false;
98 }
99
100 bool RecentTabsSubMenuModel::IsCommandIdEnabled(int command_id) const {
101 return command_id == IDC_RESTORE_TAB ?
102 chrome::IsCommandEnabled(browser_, command_id) : !model_.empty();
103 }
104
105 bool RecentTabsSubMenuModel::GetAcceleratorForCommandId(
106 int command_id, ui::Accelerator* accelerator) {
107 return command_id == IDC_RESTORE_TAB ?
108 accelerator_provider_->GetAcceleratorForCommandId(command_id,
109 accelerator) :
110 false;
111 }
112
113 void RecentTabsSubMenuModel::ExecuteCommand(int command_id) {
114 ExecuteCommand(command_id, 0);
115 }
116
117 void RecentTabsSubMenuModel::ExecuteCommand(int command_id, int event_flags) {
118 if (command_id == IDC_RESTORE_TAB) {
119 chrome::ExecuteCommandWithDisposition(browser_, command_id,
120 chrome::DispositionFromEventFlags(event_flags));
121 return;
122 }
123
124 // An empty |model_| means there's no executable menu items.
125 // When building menu items, |command_id_| was set to zero for device section
126 // header, which is non-executable.
127 if (model_.empty() || command_id == 0)
128 return;
129
130 DCHECK_LT(command_id, static_cast<int>(model_.size()));
131 const NavigationItem& item = model_[command_id];
132 DCHECK(item.tab_id > -1 && item.url.is_valid());
133
134 if (item.session_tag.empty()) { // Restore tab of local session.
135 TabRestoreService* service =
136 TabRestoreServiceFactory::GetForProfile(browser_->profile());
137 if (!service)
138 return;
139 TabRestoreServiceDelegate* delegate =
140 TabRestoreServiceDelegate::FindDelegateForWebContents(
141 chrome::GetActiveWebContents(browser_));
142 if (!delegate)
143 return;
144 service->RestoreEntryById(delegate, item.tab_id,
145 chrome::DispositionFromEventFlags(event_flags));
146 } else { // Restore tab of foreign session.
147 browser_sync::SessionModelAssociator* associator = GetModelAssociator();
148 if (!associator)
149 return;
150 const SessionTab* tab;
151 if (!associator->GetForeignTab(item.session_tag, item.tab_id, &tab))
152 return;
153 if (tab->navigations.empty())
154 return;
155 SessionRestore::RestoreForeignSessionTab(
156 chrome::GetActiveWebContents(browser_), *tab,
157 chrome::DispositionFromEventFlags(event_flags));
158 }
159 }
160
161 void RecentTabsSubMenuModel::Build() {
162 // For now when only last opened tab can be restored (instead of multiple),
163 // |model_| only contains items for other devices:
164 // - section header for device name (command_id = 0)
165 // - tabs from other devices (command_id = index into |model_|, which will
166 // never be 0 since there would be always be a section header)
167 // - separator that separates tabs of each device (comand id = -1)
168 // Later, if multiple tabs in local session can be restored, |model_| will
169 // contain items in the entire menu, and first separator for other devices
170 // is only needed if there's at least 1 local tab to restore.
171 BuildRecentlyClosed();
172 BuildDevices();
173 if (model_.empty())
174 AddItemWithStringId(0, IDS_RECENT_TABS_NO_DEVICE_TABS);
175 }
176
177 void RecentTabsSubMenuModel::BuildRecentlyClosed() {
178 #if !defined(SHOW_MULTIPLE_RECENTLY_CLOSED_TABS)
sky 2012/11/07 00:10:47 Why the ifdef?
kuan 2012/11/07 02:29:46 i explained it in the cl description. david wrote
sky 2012/11/07 14:40:12 The ifdefs make the code a lot harder to follow. A
kuan 2012/11/07 16:48:52 Done. "Reopen closed tab" is disabled if there's
179 AddItemWithStringId(IDC_RESTORE_TAB, IDS_RESTORE_TAB);
180 AddSeparator(ui::NORMAL_SEPARATOR);
181 #else
182 TabRestoreService* service =
183 TabRestoreServiceFactory::GetForProfile(browser_->profile());
sky 2012/11/07 00:10:47 What happens if the contents of the tabrestoreserv
kuan 2012/11/07 02:29:46 since this code is not being used for now, i've ad
sky 2012/11/07 14:40:12 Yet more of a reason to nuke this code.
kuan 2012/11/07 16:48:52 Done.
184
185 const int kRecentlyClosedCount = 10;
186 const TabRestoreService::Entries& entries = service->entries();
187 int added_count = 0;
188 std::set<NavigationItem> seen_tabs;
sky 2012/11/07 00:10:47 Is there a particular reason for trying to avoid c
kuan 2012/11/07 02:29:46 i believe david didn't want to repeat tabs with th
189 for (TabRestoreService::Entries::const_iterator it = entries.begin();
190 it != entries.end() && added_count < kRecentlyClosedCount; ++it) {
191 TabRestoreService::Entry* entry = *it;
192
193 if (entry->type == TabRestoreService::WINDOW) {
194 TabRestoreService::Window* window_entry =
195 static_cast<TabRestoreService::Window*>(entry);
196 std::vector<TabRestoreService::Tab>& tabs = window_entry->tabs;
197 if (tabs.empty())
198 continue;
199
200 // Loop over the window's tabs and add them to the menu.
201 std::vector<TabRestoreService::Tab>::const_iterator it;
sky 2012/11/07 00:10:47 Use a different name thant that of it defined on 1
kuan 2012/11/07 02:29:46 Done.
202 for (it = tabs.begin(); it != tabs.end(); ++it) {
203 TabRestoreService::Tab tab = *it;
204 if (BuildRecentlyClosedTabItem(tab, &seen_tabs))
sky 2012/11/07 00:10:47 Why are you extracting the tabs from window entrie
kuan 2012/11/07 02:29:46 i assume u're talking about RecentClosedTabsHandle
sky 2012/11/07 17:35:02 I'm referring to why do you look through all the t
kuan 2012/11/07 17:38:23 this code is nuked in patch set 3.
205 ++added_count;
206 }
207 } else if (entry->type == TabRestoreService::TAB) {
208 TabRestoreService::Tab* tab = static_cast<TabRestoreService::Tab*>(entry);
209 if (!tab)
sky 2012/11/07 00:10:47 If this were NULL you would have crashed on 193.
kuan 2012/11/07 02:29:46 hm.. i thot that even if entry is !NULL, if it's t
sky 2012/11/07 14:40:12 No, you would basically be casting to the wrong th
kuan 2012/11/07 16:48:52 Done. code is nuked.
210 continue;
211
212 if (BuildRecentlyClosedTabItem(*tab, &seen_tabs))
213 ++added_count;
214 }
215 }
216 #endif // defined(SHOW_MULTIPLE_RECENTLY_CLOSED_TABS)
217 }
218
219 bool RecentTabsSubMenuModel::BuildRecentlyClosedTabItem(
220 const TabRestoreService::Tab& entry,
221 std::set<NavigationItem>* seen_tabs) {
222 #if !defined(SHOW_MULTIPLE_RECENTLY_CLOSED_TABS)
223 return false;
224 #else
225 const TabNavigation& current_navigation =
226 entry.navigations[entry.current_navigation_index];
227 NavigationItem item(std::string(), entry.id,
228 current_navigation.virtual_url());
229 if (seen_tabs->count(item))
230 return false;
sky 2012/11/07 00:10:47 nit: indentation is off.
kuan 2012/11/07 02:29:46 Done.
231 BuildTabToRestore(item, current_navigation.title());
232 seen_tabs->insert(item);
233 return true;
234 #endif // defined(SHOW_MULTIPLE_RECENTLY_CLOSED_TABS)
235 }
236
237 bool RecentTabsSubMenuModel::BuildForeignTabItem(
238 const std::string& session_tag,
239 const SessionTab& tab,
240 const std::string& session_name,
241 bool need_separator) {
242 if (tab.navigations.empty())
243 return false;
244 int selected_index = std::min(tab.current_navigation_index,
245 static_cast<int>(tab.navigations.size() - 1));
246 const TabNavigation& current_navigation = tab.navigations.at(selected_index);
247 const GURL& tab_url = current_navigation.virtual_url();
248 if (tab_url == GURL(chrome::kChromeUINewTabURL))
249 return false;
250 if (need_separator) {
251 AddSeparator(ui::NORMAL_SEPARATOR);
252 model_.push_back(NavigationItem());
253 }
254 if (!session_name.empty()) {
255 AddItem(0, UTF8ToUTF16(session_name));
256 model_.push_back(NavigationItem());
257 }
258 NavigationItem item(session_tag, tab.tab_id.id(),
259 current_navigation.virtual_url());
260 BuildTabToRestore(item, current_navigation.title());
261 return true;
262 }
263
264 void RecentTabsSubMenuModel::BuildTabToRestore(const NavigationItem& item,
265 const string16& tab_title) {
266 AddItem(model_.size(), tab_title);
267 AddFavicon(model_.size(), item.url);
268 model_.push_back(item);
269 }
270
271 void RecentTabsSubMenuModel::AddFavicon(int model_index, const GURL& url) {
272 if (!default_favicon_) {
273 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
274 default_favicon_ = &rb.GetNativeImageNamed(IDR_DEFAULT_FAVICON);
275 }
276 // Set default icon first.
277 SetIcon(GetIndexInMenu(model_index), *default_favicon_);
278 // Start request to fetch actual icon if possible.
279 FaviconService* favicon_service = FaviconServiceFactory::GetForProfile(
280 browser_->profile(), Profile::EXPLICIT_ACCESS);
281 if (!favicon_service)
282 return;
283 FaviconService::Handle handle = favicon_service->GetFaviconImageForURL(
284 FaviconService::FaviconForURLParams(browser_->profile(), url,
sky 2012/11/07 00:10:47 nit: indentation is off here.
kuan 2012/11/07 02:29:46 Done.
285 history::FAVICON, gfx::kFaviconSize, &favicon_consumer_),
286 base::Bind(&RecentTabsSubMenuModel::OnFaviconDataAvailable,
287 weak_ptr_factory_.GetWeakPtr()));
288 favicon_consumer_.SetClientData(favicon_service, handle, model_index);
289 }
290
291 void RecentTabsSubMenuModel::OnFaviconDataAvailable(
292 FaviconService::Handle handle,
293 const history::FaviconImageResult& image_result) {
294 if (image_result.image.IsEmpty())
295 return;
296 DCHECK(!model_.empty());
297 int model_index = favicon_consumer_.GetClientData(
298 FaviconServiceFactory::GetForProfile(browser_->profile(),
299 Profile::EXPLICIT_ACCESS),
300 handle);
301 DCHECK(model_index > 0 && model_index < static_cast<int>(model_.size()));
302 DCHECK(model_[model_index].tab_id > -1 && model_[model_index].url.is_valid());
303 int index_in_menu = GetIndexInMenu(model_index);
304 SetIcon(index_in_menu, image_result.image);
305 if (menu_model_delegate())
306 menu_model_delegate()->OnIconChanged(index_in_menu);
307 }
308
309 void RecentTabsSubMenuModel::BuildDevices() {
310 browser_sync::SessionModelAssociator* associator = GetModelAssociator();
311 if (!associator)
312 return;
313
314 std::vector<const browser_sync::SyncedSession*> sessions;
315 if (!associator->GetAllForeignSessions(&sessions))
316 return;
317
318 // Sort sessions from most recent to least recent.
319 std::sort(sessions.begin(), sessions.end(), SortSessionsByRecency);
320
321 const size_t kMaxSessionsToShow = 10;
322 bool need_separator = false;
323 for (size_t i = 0; i < sessions.size() && i < kMaxSessionsToShow; ++i) {
324 const browser_sync::SyncedSession* session = sessions[i];
325 const std::string& session_tag = session->session_tag;
326
327 // Get windows of session.
328 std::vector<const SessionWindow*> windows;
329 if (!associator->GetForeignSession(session_tag, &windows) ||
330 windows.empty()) {
331 continue;
332 }
333 // Sort windows from most recent to least recent, based on tabs in windows.
334 std::sort(windows.begin(), windows.end(), SortWindowsByRecency);
335
336 const int kMaxTabsPerSessionToShow = 18;
337 int num_tabs_in_session = 0;
338 for (size_t j = 0;
339 j < windows.size() && num_tabs_in_session < kMaxTabsPerSessionToShow;
340 ++j) {
341 const SessionWindow* window = windows[j];
342 // Get tabs of window.
343 for (size_t k = 0;
344 k < window->tabs.size() &&
345 num_tabs_in_session < kMaxTabsPerSessionToShow;
346 ++k) {
347 if (BuildForeignTabItem(session_tag, *window->tabs[k],
348 // Only need |session_name| for the first tab of the session.
349 !num_tabs_in_session ? session->session_name : std::string(),
350 need_separator)) {
351 need_separator = false;
352 ++num_tabs_in_session;
353 }
354 } // for all tabs of one window
355 } // for all windows of one session
356
357 if (num_tabs_in_session > 0)
358 need_separator = true;
359 } // for all sessions
360 }
361
362 int RecentTabsSubMenuModel::GetIndexInMenu(int model_index) const {
363 // For now when only last closed tab can be restored, |model_| does not
364 // include IDC_RESTORE_TAB and the separator after.
365 const int kNumItemsNotInModel = 2;
366 // |model_index| indexes into |model_|, which doesn't include
367 // kNumItemsNotInModel, so add that to get index in menu.
368 return model_index + kNumItemsNotInModel;
369 }
370
371 browser_sync::SessionModelAssociator*
372 RecentTabsSubMenuModel::GetModelAssociator() const {
373 ProfileSyncService* service = ProfileSyncServiceFactory::GetInstance()->
374 GetForProfile(browser_->profile());
375 // Only return the associator if it exists and it is done syncing sessions.
376 return service && service->ShouldPushChanges() ?
377 service->GetSessionModelAssociator() : NULL;
378 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698