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

Side by Side Diff: chrome/browser/ui/webui/ntp/foreign_session_handler.cc

Issue 1140813003: NTP Zombie Code Slayer III, The Saga Continues: Foreign Sessions (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@suggestions-page
Patch Set: Created 5 years, 7 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
OLDNEW
(Empty)
1 // Copyright (c) 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/webui/ntp/foreign_session_handler.h"
6
7 #include <algorithm>
8 #include <string>
9 #include <vector>
10
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/i18n/time_formatting.h"
14 #include "base/memory/scoped_vector.h"
15 #include "base/prefs/pref_service.h"
16 #include "base/prefs/scoped_user_pref_update.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/values.h"
20 #include "chrome/browser/chrome_notification_types.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/sessions/session_restore.h"
23 #include "chrome/browser/sync/profile_sync_service.h"
24 #include "chrome/browser/sync/profile_sync_service_factory.h"
25 #include "chrome/browser/ui/host_desktop.h"
26 #include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
27 #include "chrome/common/pref_names.h"
28 #include "chrome/common/url_constants.h"
29 #include "chrome/grit/generated_resources.h"
30 #include "components/pref_registry/pref_registry_syncable.h"
31 #include "content/public/browser/notification_service.h"
32 #include "content/public/browser/notification_source.h"
33 #include "content/public/browser/url_data_source.h"
34 #include "content/public/browser/web_contents.h"
35 #include "content/public/browser/web_ui.h"
36 #include "ui/base/l10n/l10n_util.h"
37 #include "ui/base/l10n/time_format.h"
38 #include "ui/base/webui/web_ui_util.h"
39
40 namespace browser_sync {
41
42 // Maximum number of sessions we're going to display on the NTP
43 static const size_t kMaxSessionsToShow = 10;
44
45 namespace {
46
47 // Comparator function for use with std::sort that will sort sessions by
48 // descending modified_time (i.e., most recent first).
49 bool SortSessionsByRecency(const SyncedSession* s1, const SyncedSession* s2) {
50 return s1->modified_time > s2->modified_time;
51 }
52
53 } // namepace
54
55 ForeignSessionHandler::ForeignSessionHandler() {
56 }
57
58 // static
59 void ForeignSessionHandler::RegisterProfilePrefs(
60 user_prefs::PrefRegistrySyncable* registry) {
61 registry->RegisterDictionaryPref(prefs::kNtpCollapsedForeignSessions);
62 }
63
64 // static
65 void ForeignSessionHandler::OpenForeignSessionTab(
66 content::WebUI* web_ui,
67 const std::string& session_string_value,
68 SessionID::id_type window_num,
69 SessionID::id_type tab_id,
70 const WindowOpenDisposition& disposition) {
71 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(web_ui);
72 if (!open_tabs)
73 return;
74
75 // We don't actually care about |window_num|, this is just a sanity check.
76 DCHECK_LT(kInvalidId, window_num);
77 const ::sessions::SessionTab* tab;
78 if (!open_tabs->GetForeignTab(session_string_value, tab_id, &tab)) {
79 LOG(ERROR) << "Failed to load foreign tab.";
80 return;
81 }
82 if (tab->navigations.empty()) {
83 LOG(ERROR) << "Foreign tab no longer has valid navigations.";
84 return;
85 }
86 SessionRestore::RestoreForeignSessionTab(
87 web_ui->GetWebContents(), *tab, disposition);
88 }
89
90 // static
91 void ForeignSessionHandler::OpenForeignSessionWindows(
92 content::WebUI* web_ui,
93 const std::string& session_string_value,
94 SessionID::id_type window_num) {
95 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(web_ui);
96 if (!open_tabs)
97 return;
98
99 std::vector<const ::sessions::SessionWindow*> windows;
100 // Note: we don't own the ForeignSessions themselves.
101 if (!open_tabs->GetForeignSession(session_string_value, &windows)) {
102 LOG(ERROR) << "ForeignSessionHandler failed to get session data from"
103 "OpenTabsUIDelegate.";
104 return;
105 }
106 std::vector<const ::sessions::SessionWindow*>::const_iterator iter_begin =
107 windows.begin() + (window_num == kInvalidId ? 0 : window_num);
108 std::vector<const ::sessions::SessionWindow*>::const_iterator iter_end =
109 window_num == kInvalidId ?
110 std::vector<const ::sessions::SessionWindow*>::const_iterator(
111 windows.end()) : iter_begin + 1;
112 chrome::HostDesktopType host_desktop_type =
113 chrome::GetHostDesktopTypeForNativeView(
114 web_ui->GetWebContents()->GetNativeView());
115 SessionRestore::RestoreForeignSessionWindows(
116 Profile::FromWebUI(web_ui), host_desktop_type, iter_begin, iter_end);
117 }
118
119 // static
120 bool ForeignSessionHandler::SessionTabToValue(
121 const ::sessions::SessionTab& tab,
122 base::DictionaryValue* dictionary) {
123 if (tab.navigations.empty())
124 return false;
125
126 int selected_index = std::min(tab.current_navigation_index,
127 static_cast<int>(tab.navigations.size() - 1));
128 const ::sessions::SerializedNavigationEntry& current_navigation =
129 tab.navigations.at(selected_index);
130 GURL tab_url = current_navigation.virtual_url();
131 if (tab_url == GURL(chrome::kChromeUINewTabURL))
132 return false;
133
134 NewTabUI::SetUrlTitleAndDirection(dictionary, current_navigation.title(),
135 tab_url);
136 dictionary->SetString("type", "tab");
137 dictionary->SetDouble("timestamp",
138 static_cast<double>(tab.timestamp.ToInternalValue()));
139 // TODO(jeremycho): This should probably be renamed to tabId to avoid
140 // confusion with the ID corresponding to a session. Investigate all the
141 // places (C++ and JS) where this is being used. (http://crbug.com/154865).
142 dictionary->SetInteger("sessionId", tab.tab_id.id());
143 return true;
144 }
145
146 // static
147 OpenTabsUIDelegate* ForeignSessionHandler::GetOpenTabsUIDelegate(
148 content::WebUI* web_ui) {
149 Profile* profile = Profile::FromWebUI(web_ui);
150 ProfileSyncService* service =
151 ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile);
152
153 // Only return the delegate if it exists and it is done syncing sessions.
154 if (service && service->SyncActive())
155 return service->GetOpenTabsUIDelegate();
156
157 return NULL;
158 }
159
160 void ForeignSessionHandler::RegisterMessages() {
161 Init();
162 web_ui()->RegisterMessageCallback("deleteForeignSession",
163 base::Bind(&ForeignSessionHandler::HandleDeleteForeignSession,
164 base::Unretained(this)));
165 web_ui()->RegisterMessageCallback("getForeignSessions",
166 base::Bind(&ForeignSessionHandler::HandleGetForeignSessions,
167 base::Unretained(this)));
168 web_ui()->RegisterMessageCallback("openForeignSession",
169 base::Bind(&ForeignSessionHandler::HandleOpenForeignSession,
170 base::Unretained(this)));
171 web_ui()->RegisterMessageCallback("setForeignSessionCollapsed",
172 base::Bind(&ForeignSessionHandler::HandleSetForeignSessionCollapsed,
173 base::Unretained(this)));
174 }
175
176 void ForeignSessionHandler::Init() {
177 Profile* profile = Profile::FromWebUI(web_ui());
178 ProfileSyncService* service =
179 ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile);
180 registrar_.Add(this, chrome::NOTIFICATION_SYNC_CONFIGURE_DONE,
181 content::Source<ProfileSyncService>(service));
182 registrar_.Add(this, chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED,
183 content::Source<Profile>(profile));
184 registrar_.Add(this, chrome::NOTIFICATION_FOREIGN_SESSION_DISABLED,
185 content::Source<Profile>(profile));
186 }
187
188 void ForeignSessionHandler::Observe(
189 int type,
190 const content::NotificationSource& source,
191 const content::NotificationDetails& details) {
192 base::ListValue list_value;
193
194 switch (type) {
195 case chrome::NOTIFICATION_FOREIGN_SESSION_DISABLED:
196 // Tab sync is disabled, so clean up data about collapsed sessions.
197 Profile::FromWebUI(web_ui())->GetPrefs()->ClearPref(
198 prefs::kNtpCollapsedForeignSessions);
199 // Fall through.
200 case chrome::NOTIFICATION_SYNC_CONFIGURE_DONE:
201 case chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED:
202 HandleGetForeignSessions(&list_value);
203 break;
204 default:
205 NOTREACHED();
206 }
207 }
208
209
210 bool ForeignSessionHandler::IsTabSyncEnabled() {
211 Profile* profile = Profile::FromWebUI(web_ui());
212 ProfileSyncService* service =
213 ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile);
214 return service && service->GetActiveDataTypes().Has(syncer::PROXY_TABS);
215 }
216
217 base::string16 ForeignSessionHandler::FormatSessionTime(
218 const base::Time& time) {
219 // Return a time like "1 hour ago", "2 days ago", etc.
220 base::Time now = base::Time::Now();
221 // TimeFormat does not support negative TimeDelta values, so then we use 0.
222 return ui::TimeFormat::Simple(
223 ui::TimeFormat::FORMAT_ELAPSED, ui::TimeFormat::LENGTH_SHORT,
224 now < time ? base::TimeDelta() : now - time);
225 }
226
227 void ForeignSessionHandler::HandleGetForeignSessions(
228 const base::ListValue* args) {
229 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(web_ui());
230 std::vector<const SyncedSession*> sessions;
231
232 base::ListValue session_list;
233 if (open_tabs && open_tabs->GetAllForeignSessions(&sessions)) {
234 // Sort sessions from most recent to least recent.
235 std::sort(sessions.begin(), sessions.end(), SortSessionsByRecency);
236
237 // Use a pref to keep track of sessions that were collapsed by the user.
238 // To prevent the pref from accumulating stale sessions, clear it each time
239 // and only add back sessions that are still current.
240 DictionaryPrefUpdate pref_update(Profile::FromWebUI(web_ui())->GetPrefs(),
241 prefs::kNtpCollapsedForeignSessions);
242 base::DictionaryValue* current_collapsed_sessions = pref_update.Get();
243 scoped_ptr<base::DictionaryValue> collapsed_sessions(
244 current_collapsed_sessions->DeepCopy());
245 current_collapsed_sessions->Clear();
246
247 // Note: we don't own the SyncedSessions themselves.
248 for (size_t i = 0; i < sessions.size() && i < kMaxSessionsToShow; ++i) {
249 const SyncedSession* session = sessions[i];
250 const std::string& session_tag = session->session_tag;
251 scoped_ptr<base::DictionaryValue> session_data(
252 new base::DictionaryValue());
253 // The items which are to be written into |session_data| are also
254 // described in chrome/browser/resources/ntp4/other_sessions.js in
255 // @typedef for SessionData. Please update it whenever you add or remove
256 // any keys here.
257 session_data->SetString("tag", session_tag);
258 session_data->SetString("name", session->session_name);
259 session_data->SetString("deviceType", session->DeviceTypeAsString());
260 session_data->SetString("modifiedTime",
261 FormatSessionTime(session->modified_time));
262
263 bool is_collapsed = collapsed_sessions->HasKey(session_tag);
264 session_data->SetBoolean("collapsed", is_collapsed);
265 if (is_collapsed)
266 current_collapsed_sessions->SetBoolean(session_tag, true);
267
268 scoped_ptr<base::ListValue> window_list(new base::ListValue());
269 for (SyncedSession::SyncedWindowMap::const_iterator it =
270 session->windows.begin(); it != session->windows.end(); ++it) {
271 ::sessions::SessionWindow* window = it->second;
272 scoped_ptr<base::DictionaryValue> window_data(
273 new base::DictionaryValue());
274 if (SessionWindowToValue(*window, window_data.get()))
275 window_list->Append(window_data.release());
276 }
277
278 session_data->Set("windows", window_list.release());
279 session_list.Append(session_data.release());
280 }
281 }
282 base::FundamentalValue tab_sync_enabled(IsTabSyncEnabled());
283 web_ui()->CallJavascriptFunction("ntp.setForeignSessions",
284 session_list,
285 tab_sync_enabled);
286 }
287
288 void ForeignSessionHandler::HandleOpenForeignSession(
289 const base::ListValue* args) {
290 size_t num_args = args->GetSize();
291 // Expect either 1 or 8 args. For restoring an entire session, only
292 // one argument is required -- the session tag. To restore a tab,
293 // the additional args required are the window id, the tab id,
294 // and 4 properties of the event object (button, altKey, ctrlKey,
295 // metaKey, shiftKey) for determining how to open the tab.
296 if (num_args != 8U && num_args != 1U) {
297 LOG(ERROR) << "openForeignSession called with " << args->GetSize()
298 << " arguments.";
299 return;
300 }
301
302 // Extract the session tag (always provided).
303 std::string session_string_value;
304 if (!args->GetString(0, &session_string_value)) {
305 LOG(ERROR) << "Failed to extract session tag.";
306 return;
307 }
308
309 // Extract window number.
310 std::string window_num_str;
311 int window_num = kInvalidId;
312 if (num_args >= 2 && (!args->GetString(1, &window_num_str) ||
313 !base::StringToInt(window_num_str, &window_num))) {
314 LOG(ERROR) << "Failed to extract window number.";
315 return;
316 }
317
318 // Extract tab id.
319 std::string tab_id_str;
320 SessionID::id_type tab_id = kInvalidId;
321 if (num_args >= 3 && (!args->GetString(2, &tab_id_str) ||
322 !base::StringToInt(tab_id_str, &tab_id))) {
323 LOG(ERROR) << "Failed to extract tab SessionID.";
324 return;
325 }
326
327 if (tab_id != kInvalidId) {
328 WindowOpenDisposition disposition = webui::GetDispositionFromClick(args, 3);
329 OpenForeignSessionTab(
330 web_ui(), session_string_value, window_num, tab_id, disposition);
331 } else {
332 OpenForeignSessionWindows(web_ui(), session_string_value, window_num);
333 }
334 }
335
336 void ForeignSessionHandler::HandleDeleteForeignSession(
337 const base::ListValue* args) {
338 if (args->GetSize() != 1U) {
339 LOG(ERROR) << "Wrong number of args to deleteForeignSession";
340 return;
341 }
342
343 // Get the session tag argument (required).
344 std::string session_tag;
345 if (!args->GetString(0, &session_tag)) {
346 LOG(ERROR) << "Unable to extract session tag";
347 return;
348 }
349
350 OpenTabsUIDelegate* open_tabs = GetOpenTabsUIDelegate(web_ui());
351 if (open_tabs)
352 open_tabs->DeleteForeignSession(session_tag);
353 }
354
355 void ForeignSessionHandler::HandleSetForeignSessionCollapsed(
356 const base::ListValue* args) {
357 if (args->GetSize() != 2U) {
358 LOG(ERROR) << "Wrong number of args to setForeignSessionCollapsed";
359 return;
360 }
361
362 // Get the session tag argument (required).
363 std::string session_tag;
364 if (!args->GetString(0, &session_tag)) {
365 LOG(ERROR) << "Unable to extract session tag";
366 return;
367 }
368
369 bool is_collapsed;
370 if (!args->GetBoolean(1, &is_collapsed)) {
371 LOG(ERROR) << "Unable to extract boolean argument";
372 return;
373 }
374
375 // Store session tags for collapsed sessions in a preference so that the
376 // collapsed state persists.
377 PrefService* prefs = Profile::FromWebUI(web_ui())->GetPrefs();
378 DictionaryPrefUpdate update(prefs, prefs::kNtpCollapsedForeignSessions);
379 if (is_collapsed)
380 update.Get()->SetBoolean(session_tag, true);
381 else
382 update.Get()->Remove(session_tag, NULL);
383 }
384
385 bool ForeignSessionHandler::SessionWindowToValue(
386 const ::sessions::SessionWindow& window,
387 base::DictionaryValue* dictionary) {
388 if (window.tabs.empty()) {
389 NOTREACHED();
390 return false;
391 }
392 scoped_ptr<base::ListValue> tab_values(new base::ListValue());
393 // Calculate the last |modification_time| for all entries within a window.
394 base::Time modification_time = window.timestamp;
395 for (size_t i = 0; i < window.tabs.size(); ++i) {
396 scoped_ptr<base::DictionaryValue> tab_value(new base::DictionaryValue());
397 if (SessionTabToValue(*window.tabs[i], tab_value.get())) {
398 modification_time = std::max(modification_time,
399 window.tabs[i]->timestamp);
400 tab_values->Append(tab_value.release());
401 }
402 }
403 if (tab_values->GetSize() == 0)
404 return false;
405 // The items which are to be written into |dictionary| are also described in
406 // chrome/browser/resources/ntp4/other_sessions.js in @typedef for WindowData.
407 // Please update it whenever you add or remove any keys here.
408 dictionary->SetString("type", "window");
409 dictionary->SetDouble("timestamp", modification_time.ToInternalValue());
410 const base::TimeDelta last_synced = base::Time::Now() - modification_time;
411 // If clock skew leads to a future time, or we last synced less than a minute
412 // ago, output "Just now".
413 dictionary->SetString("userVisibleTimestamp",
414 last_synced < base::TimeDelta::FromMinutes(1) ?
415 l10n_util::GetStringUTF16(IDS_SYNC_TIME_JUST_NOW) :
416 ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_ELAPSED,
417 ui::TimeFormat::LENGTH_SHORT, last_synced));
418 dictionary->SetInteger("sessionId", window.window_id.id());
419 dictionary->Set("tabs", tab_values.release());
420 return true;
421 }
422
423 } // namespace browser_sync
OLDNEW
« no previous file with comments | « chrome/browser/ui/webui/ntp/foreign_session_handler.h ('k') | chrome/browser/ui/webui/ntp/new_tab_ui.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698