1 // Copyright (c) 2011 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/foreign_session_handler.h"
6
7 #include <algorithm>
8 #include <string>
9 #include <vector>
10 #include "base/memory/scoped_vector.h"
11 #include "base/string_number_conversions.h"
12 #include "base/utf_string_conversions.h"
13 #include "base/values.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/sessions/session_restore.h"
16 #include "chrome/browser/sync/engine/syncapi.h"
17 #include "chrome/browser/sync/profile_sync_service.h"
18 #include "chrome/browser/ui/webui/new_tab_ui.h"
19 #include "content/common/notification_details.h"
20 #include "content/common/notification_service.h"
21 #include "chrome/common/url_constants.h"
22
23 namespace browser_sync {
24
25 // Maximum number of session we're going to display on the NTP
26 static const int kMaxSessionsToShow = 10;
27
28 // Invalid value, used to note that we don't have a tab or window number.
29 static const int kInvalidId = -1;
30
ForeignSessionHandler()31 ForeignSessionHandler::ForeignSessionHandler() {
32 Init();
33 }
34
RegisterMessages()35 void ForeignSessionHandler::RegisterMessages() {
36 web_ui_->RegisterMessageCallback("getForeignSessions",
37 NewCallback(this,
38 &ForeignSessionHandler::HandleGetForeignSessions));
39 web_ui_->RegisterMessageCallback("openForeignSession",
40 NewCallback(this,
41 &ForeignSessionHandler::HandleOpenForeignSession));
42 }
43
Init()44 void ForeignSessionHandler::Init() {
45 registrar_.Add(this, NotificationType::SYNC_CONFIGURE_DONE,
46 NotificationService::AllSources());
47 registrar_.Add(this, NotificationType::FOREIGN_SESSION_UPDATED,
48 NotificationService::AllSources());
49 registrar_.Add(this, NotificationType::FOREIGN_SESSION_DISABLED,
50 NotificationService::AllSources());
51 }
52
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)53 void ForeignSessionHandler::Observe(NotificationType type,
54 const NotificationSource& source,
55 const NotificationDetails& details) {
56 ListValue list_value;
57 switch (type.value) {
58 case NotificationType::SYNC_CONFIGURE_DONE:
59 case NotificationType::FOREIGN_SESSION_UPDATED:
60 HandleGetForeignSessions(&list_value);
61 break;
62 case NotificationType::FOREIGN_SESSION_DISABLED:
63 // Calling foreignSessions with empty list will automatically hide
64 // foreign session section.
65 web_ui_->CallJavascriptFunction("foreignSessions", list_value);
66 break;
67 default:
68 NOTREACHED();
69 }
70 }
71
GetModelAssociator()72 SessionModelAssociator* ForeignSessionHandler::GetModelAssociator() {
73 ProfileSyncService* service = web_ui_->GetProfile()->GetProfileSyncService();
74 if (service == NULL)
75 return NULL;
76 // We only want to set the model associator if there is one, and it is done
77 // syncing sessions.
78 SessionModelAssociator* model_associator = service->
79 GetSessionModelAssociator();
80 if (model_associator == NULL ||
81 !service->ShouldPushChanges()) {
82 return NULL;
83 }
84 return web_ui_->GetProfile()->GetProfileSyncService()->
85 GetSessionModelAssociator();
86 }
87
HandleGetForeignSessions(const ListValue * args)88 void ForeignSessionHandler::HandleGetForeignSessions(const ListValue* args) {
89 SessionModelAssociator* associator = GetModelAssociator();
90 std::vector<const ForeignSession*> sessions;
91
92 if (associator == NULL) {
93 // Called before associator created, exit.
94 return;
95 }
96
97 // Note: we don't own the ForeignSessions themselves.
98 if (!associator->GetAllForeignSessions(&sessions)) {
99 LOG(ERROR) << "ForeignSessionHandler failed to get session data from"
100 "SessionModelAssociator.";
101 return;
102 }
103 int added_count = 0;
104 ListValue session_list;
105 for (std::vector<const ForeignSession*>::const_iterator i =
106 sessions.begin(); i != sessions.end() &&
107 added_count < kMaxSessionsToShow; ++i) {
108 const ForeignSession* foreign_session = *i;
109 scoped_ptr<ListValue> window_list(new ListValue());
110 for (std::vector<SessionWindow*>::const_iterator it =
111 foreign_session->windows.begin(); it != foreign_session->windows.end();
112 ++it) {
113 SessionWindow* window = *it;
114 scoped_ptr<DictionaryValue> window_data(new DictionaryValue());
115 if (SessionWindowToValue(*window, window_data.get())) {
116 window_data->SetString("sessionTag",
117 foreign_session->foreign_session_tag);
118
119 // Give ownership to |list_value|.
120 window_list->Append(window_data.release());
121 }
122 }
123 added_count++;
124
125 // Give ownership to |session_list|.
126 session_list.Append(window_list.release());
127 }
128 web_ui_->CallJavascriptFunction("foreignSessions", session_list);
129 }
130
HandleOpenForeignSession(const ListValue * args)131 void ForeignSessionHandler::HandleOpenForeignSession(
132 const ListValue* args) {
133 size_t num_args = args->GetSize();
134 if (num_args > 3U || num_args == 0) {
135 LOG(ERROR) << "openForeignWindow called with only " << args->GetSize()
136 << " arguments.";
137 return;
138 }
139
140 // Extract the machine tag (always provided).
141 std::string session_string_value;
142 if (!args->GetString(0, &session_string_value)) {
143 LOG(ERROR) << "Failed to extract session tag.";
144 return;
145 }
146
147 // Extract window number.
148 std::string window_num_str;
149 int window_num = kInvalidId;
150 if (num_args >= 2 && (!args->GetString(1, &window_num_str) ||
151 !base::StringToInt(window_num_str, &window_num))) {
152 LOG(ERROR) << "Failed to extract window number.";
153 return;
154 }
155
156 // Extract tab id.
157 std::string tab_id_str;
158 SessionID::id_type tab_id = kInvalidId;
159 if (num_args == 3 && (!args->GetString(2, &tab_id_str) ||
160 !base::StringToInt(tab_id_str, &tab_id))) {
161 LOG(ERROR) << "Failed to extract tab SessionID.";
162 return;
163 }
164
165 SessionModelAssociator* associator = GetModelAssociator();
166
167 if (tab_id != kInvalidId) {
168 // We don't actually care about |window_num|, this is just a sanity check.
169 DCHECK_LT(kInvalidId, window_num);
170 const SessionTab* tab;
171 if (!associator->GetForeignTab(session_string_value, tab_id, &tab)) {
172 LOG(ERROR) << "Failed to load foreign tab.";
173 return;
174 }
175 SessionRestore::RestoreForeignSessionTab(web_ui_->GetProfile(), *tab);
176 } else {
177 std::vector<SessionWindow*> windows;
178 // Note: we don't own the ForeignSessions themselves.
179 if (!associator->GetForeignSession(session_string_value, &windows)) {
180 LOG(ERROR) << "ForeignSessionHandler failed to get session data from"
181 "SessionModelAssociator.";
182 return;
183 }
184 std::vector<SessionWindow*>::const_iterator iter_begin = windows.begin() +
185 ((window_num == kInvalidId) ? 0 : window_num);
186 std::vector<SessionWindow*>::const_iterator iter_end =
187 ((window_num == kInvalidId) ?
188 std::vector<SessionWindow*>::const_iterator(windows.end()) :
189 iter_begin+1);
190 SessionRestore::RestoreForeignSessionWindows(web_ui_->GetProfile(),
191 iter_begin,
192 iter_end);
193 }
194 }
195
SessionTabToValue(const SessionTab & tab,DictionaryValue * dictionary)196 bool ForeignSessionHandler::SessionTabToValue(
197 const SessionTab& tab,
198 DictionaryValue* dictionary) {
199 if (tab.navigations.empty())
200 return false;
201 int selected_index = tab.current_navigation_index;
202 selected_index = std::max(
203 0,
204 std::min(selected_index,
205 static_cast<int>(tab.navigations.size() - 1)));
206 const TabNavigation& current_navigation =
207 tab.navigations.at(selected_index);
208 if (current_navigation.virtual_url() == GURL(chrome::kChromeUINewTabURL))
209 return false;
210 NewTabUI::SetURLTitleAndDirection(dictionary, current_navigation.title(),
211 current_navigation.virtual_url());
212 dictionary->SetString("type", "tab");
213 dictionary->SetDouble("timestamp",
214 static_cast<double>(tab.timestamp.ToInternalValue()));
215 dictionary->SetInteger("sessionId", tab.tab_id.id());
216 return true;
217 }
218
SessionWindowToValue(const SessionWindow & window,DictionaryValue * dictionary)219 bool ForeignSessionHandler::SessionWindowToValue(
220 const SessionWindow& window,
221 DictionaryValue* dictionary) {
222 if (window.tabs.empty()) {
223 NOTREACHED();
224 return false;
225 }
226 scoped_ptr<ListValue> tab_values(new ListValue());
227 for (size_t i = 0; i < window.tabs.size(); ++i) {
228 scoped_ptr<DictionaryValue> tab_value(new DictionaryValue());
229 if (SessionTabToValue(*window.tabs[i], tab_value.get()))
230 tab_values->Append(tab_value.release());
231 }
232 if (tab_values->GetSize() == 0)
233 return false;
234 dictionary->SetString("type", "window");
235 dictionary->SetDouble("timestamp",
236 static_cast<double>(window.timestamp.ToInternalValue()));
237 dictionary->SetInteger("sessionId", window.window_id.id());
238 dictionary->Set("tabs", tab_values.release());
239 return true;
240 }
241
242 } // namespace browser_sync
243