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 31 ForeignSessionHandler::ForeignSessionHandler() { 32 Init(); 33 } 34 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 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 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 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 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 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 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 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