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 <set> 6 7 #include "chrome/browser/profiles/profile_manager.h" 8 9 #include "base/command_line.h" 10 #include "base/file_util.h" 11 #include "base/path_service.h" 12 #include "base/string_util.h" 13 #include "chrome/browser/browser_process.h" 14 #include "chrome/browser/ui/browser_list.h" 15 #include "chrome/browser/ui/browser_window.h" 16 #include "chrome/common/chrome_constants.h" 17 #include "chrome/common/chrome_paths.h" 18 #include "chrome/common/chrome_switches.h" 19 #include "chrome/common/logging_chrome.h" 20 #include "content/browser/browser_thread.h" 21 #include "content/common/notification_service.h" 22 #include "content/common/notification_type.h" 23 #include "grit/generated_resources.h" 24 #include "net/http/http_transaction_factory.h" 25 #include "net/url_request/url_request_context.h" 26 #include "net/url_request/url_request_context_getter.h" 27 #include "net/url_request/url_request_job.h" 28 #include "net/url_request/url_request_job_tracker.h" 29 30 #if defined(OS_CHROMEOS) 31 #include "chrome/browser/chromeos/cros/cros_library.h" 32 #include "chrome/browser/chromeos/cros/cryptohome_library.h" 33 #endif 34 35 namespace { 36 37 void SuspendURLRequestJobs() { 38 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 39 for (net::URLRequestJobTracker::JobIterator i = 40 net::g_url_request_job_tracker.begin(); 41 i != net::g_url_request_job_tracker.end(); ++i) 42 (*i)->Kill(); 43 } 44 45 void SuspendRequestContext( 46 net::URLRequestContextGetter* request_context_getter) { 47 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 48 49 scoped_refptr<net::URLRequestContext> request_context = 50 request_context_getter->GetURLRequestContext(); 51 52 request_context->http_transaction_factory()->Suspend(true); 53 } 54 55 void ResumeRequestContext( 56 net::URLRequestContextGetter* request_context_getter) { 57 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 58 59 scoped_refptr<net::URLRequestContext> request_context = 60 request_context_getter->GetURLRequestContext(); 61 request_context->http_transaction_factory()->Suspend(false); 62 } 63 64 } // namespace 65 66 // static 67 void ProfileManager::ShutdownSessionServices() { 68 ProfileManager* pm = g_browser_process->profile_manager(); 69 if (!pm) // Is NULL when running unit tests. 70 return; 71 std::vector<Profile*> profiles(pm->GetLoadedProfiles()); 72 for (size_t i = 0; i < profiles.size(); ++i) 73 profiles[i]->ShutdownSessionService(); 74 } 75 76 // static 77 Profile* ProfileManager::GetDefaultProfile() { 78 FilePath user_data_dir; 79 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); 80 ProfileManager* profile_manager = g_browser_process->profile_manager(); 81 return profile_manager->GetDefaultProfile(user_data_dir); 82 } 83 84 ProfileManager::ProfileManager() : logged_in_(false) { 85 ui::SystemMonitor::Get()->AddObserver(this); 86 #if defined(OS_CHROMEOS) 87 registrar_.Add( 88 this, 89 NotificationType::LOGIN_USER_CHANGED, 90 NotificationService::AllSources()); 91 #endif 92 } 93 94 ProfileManager::~ProfileManager() { 95 ui::SystemMonitor* system_monitor = ui::SystemMonitor::Get(); 96 if (system_monitor) 97 system_monitor->RemoveObserver(this); 98 } 99 100 FilePath ProfileManager::GetDefaultProfileDir( 101 const FilePath& user_data_dir) { 102 FilePath default_profile_dir(user_data_dir); 103 default_profile_dir = 104 default_profile_dir.AppendASCII(chrome::kNotSignedInProfile); 105 return default_profile_dir; 106 } 107 108 FilePath ProfileManager::GetProfilePrefsPath( 109 const FilePath &profile_dir) { 110 FilePath default_prefs_path(profile_dir); 111 default_prefs_path = default_prefs_path.Append(chrome::kPreferencesFilename); 112 return default_prefs_path; 113 } 114 115 FilePath ProfileManager::GetCurrentProfileDir() { 116 FilePath relative_profile_dir; 117 #if defined(OS_CHROMEOS) 118 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 119 if (logged_in_) { 120 FilePath profile_dir; 121 // If the user has logged in, pick up the new profile. 122 if (command_line.HasSwitch(switches::kLoginProfile)) { 123 profile_dir = command_line.GetSwitchValuePath(switches::kLoginProfile); 124 } else { 125 // We should never be logged in with no profile dir. 126 NOTREACHED(); 127 return FilePath(""); 128 } 129 relative_profile_dir = relative_profile_dir.Append(profile_dir); 130 return relative_profile_dir; 131 } 132 #endif 133 relative_profile_dir = 134 relative_profile_dir.AppendASCII(chrome::kNotSignedInProfile); 135 return relative_profile_dir; 136 } 137 138 Profile* ProfileManager::GetDefaultProfile(const FilePath& user_data_dir) { 139 FilePath default_profile_dir(user_data_dir); 140 default_profile_dir = default_profile_dir.Append(GetCurrentProfileDir()); 141 #if defined(OS_CHROMEOS) 142 if (!logged_in_) { 143 Profile* profile; 144 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 145 146 // For cros, return the OTR profile so we never accidentally keep 147 // user data in an unencrypted profile. But doing this makes 148 // many of the browser and ui tests fail. We do return the OTR profile 149 // if the login-profile switch is passed so that we can test this. 150 // TODO(davemoore) Fix the tests so they allow OTR profiles. 151 if (!command_line.HasSwitch(switches::kTestType) || 152 command_line.HasSwitch(switches::kLoginProfile)) { 153 // Don't init extensions for this profile 154 profile = GetProfile(default_profile_dir); 155 profile = profile->GetOffTheRecordProfile(); 156 } else { 157 profile = GetProfile(default_profile_dir); 158 } 159 return profile; 160 } 161 #endif 162 return GetProfile(default_profile_dir); 163 } 164 165 Profile* ProfileManager::GetProfileWithId(ProfileId profile_id) { 166 DCHECK_NE(Profile::kInvalidProfileId, profile_id); 167 for (ProfilesInfoMap::iterator iter = profiles_info_.begin(); 168 iter != profiles_info_.end(); ++iter) { 169 if (iter->second->created) { 170 Profile* candidate = iter->second->profile.get(); 171 if (candidate->GetRuntimeId() == profile_id) 172 return candidate; 173 if (candidate->HasOffTheRecordProfile()) { 174 candidate = candidate->GetOffTheRecordProfile(); 175 if (candidate->GetRuntimeId() == profile_id) 176 return candidate; 177 } 178 } 179 } 180 return NULL; 181 } 182 183 bool ProfileManager::IsValidProfile(Profile* profile) { 184 for (ProfilesInfoMap::iterator iter = profiles_info_.begin(); 185 iter != profiles_info_.end(); ++iter) { 186 if (iter->second->created) { 187 Profile* candidate = iter->second->profile.get(); 188 if (candidate == profile || 189 (candidate->HasOffTheRecordProfile() && 190 candidate->GetOffTheRecordProfile() == profile)) { 191 return true; 192 } 193 } 194 } 195 return false; 196 } 197 198 std::vector<Profile*> ProfileManager::GetLoadedProfiles() const { 199 std::vector<Profile*> profiles; 200 for (ProfilesInfoMap::const_iterator iter = profiles_info_.begin(); 201 iter != profiles_info_.end(); ++iter) { 202 if (iter->second->created) 203 profiles.push_back(iter->second->profile.get()); 204 } 205 return profiles; 206 } 207 208 Profile* ProfileManager::GetProfile(const FilePath& profile_dir) { 209 // If the profile is already loaded (e.g., chrome.exe launched twice), just 210 // return it. 211 Profile* profile = GetProfileByPath(profile_dir); 212 if (NULL != profile) 213 return profile; 214 215 profile = Profile::CreateProfile(profile_dir); 216 DCHECK(profile); 217 if (profile) { 218 bool result = AddProfile(profile); 219 DCHECK(result); 220 } 221 return profile; 222 } 223 224 void ProfileManager::CreateProfileAsync(const FilePath& user_data_dir, 225 Observer* observer) { 226 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 227 ProfilesInfoMap::iterator iter = profiles_info_.find(user_data_dir); 228 if (iter != profiles_info_.end()) { 229 ProfileInfo* info = iter->second.get(); 230 if (info->created) { 231 // Profile has already been created. Call observer immediately. 232 observer->OnProfileCreated(info->profile.get()); 233 } else { 234 // Profile is being created. Add observer to list. 235 info->observers.push_back(observer); 236 } 237 } else { 238 // Initiate asynchronous creation process. 239 ProfileInfo* info = 240 RegisterProfile(Profile::CreateProfileAsync(user_data_dir, this), 241 false); 242 info->observers.push_back(observer); 243 } 244 } 245 246 // static 247 void ProfileManager::CreateDefaultProfileAsync(Observer* observer) { 248 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 249 ProfileManager* profile_manager = g_browser_process->profile_manager(); 250 251 FilePath default_profile_dir; 252 PathService::Get(chrome::DIR_USER_DATA, &default_profile_dir); 253 default_profile_dir = default_profile_dir.Append( 254 profile_manager->GetCurrentProfileDir()); 255 256 profile_manager->CreateProfileAsync(default_profile_dir, 257 observer); 258 } 259 260 bool ProfileManager::AddProfile(Profile* profile) { 261 DCHECK(profile); 262 263 // Make sure that we're not loading a profile with the same ID as a profile 264 // that's already loaded. 265 if (GetProfileByPath(profile->GetPath())) { 266 NOTREACHED() << "Attempted to add profile with the same path (" << 267 profile->GetPath().value() << 268 ") as an already-loaded profile."; 269 return false; 270 } 271 272 RegisterProfile(profile, true); 273 DoFinalInit(profile); 274 return true; 275 } 276 277 ProfileManager::ProfileInfo* ProfileManager::RegisterProfile(Profile* profile, 278 bool created) { 279 ProfileInfo* info = new ProfileInfo(profile, created); 280 ProfilesInfoMap::iterator new_elem = 281 (profiles_info_.insert(std::make_pair(profile->GetPath(), info))).first; 282 return info; 283 } 284 285 Profile* ProfileManager::GetProfileByPath(const FilePath& path) const { 286 ProfilesInfoMap::const_iterator iter = profiles_info_.find(path); 287 return (iter == profiles_info_.end()) ? NULL : iter->second->profile.get(); 288 } 289 290 void ProfileManager::OnSuspend() { 291 DCHECK(CalledOnValidThread()); 292 293 bool posted = BrowserThread::PostTask( 294 BrowserThread::IO, FROM_HERE, 295 NewRunnableFunction(&SuspendURLRequestJobs)); 296 DCHECK(posted); 297 298 scoped_refptr<net::URLRequestContextGetter> request_context; 299 std::vector<Profile*> profiles(GetLoadedProfiles()); 300 for (size_t i = 0; i < profiles.size(); ++i) { 301 request_context = profiles[i]->GetRequestContext(); 302 posted = BrowserThread::PostTask( 303 BrowserThread::IO, FROM_HERE, 304 NewRunnableFunction(&SuspendRequestContext, request_context)); 305 DCHECK(posted); 306 request_context = profiles[i]->GetRequestContextForMedia(); 307 posted = BrowserThread::PostTask( 308 BrowserThread::IO, FROM_HERE, 309 NewRunnableFunction(&SuspendRequestContext, request_context)); 310 DCHECK(posted); 311 } 312 } 313 314 void ProfileManager::OnResume() { 315 DCHECK(CalledOnValidThread()); 316 317 scoped_refptr<net::URLRequestContextGetter> request_context; 318 std::vector<Profile*> profiles(GetLoadedProfiles()); 319 for (size_t i = 0; i < profiles.size(); ++i) { 320 request_context = profiles[i]->GetRequestContext(); 321 bool posted = BrowserThread::PostTask( 322 BrowserThread::IO, FROM_HERE, 323 NewRunnableFunction(&ResumeRequestContext, request_context)); 324 DCHECK(posted); 325 request_context = profiles[i]->GetRequestContextForMedia(); 326 posted = BrowserThread::PostTask( 327 BrowserThread::IO, FROM_HERE, 328 NewRunnableFunction(&ResumeRequestContext, request_context)); 329 DCHECK(posted); 330 } 331 } 332 333 void ProfileManager::Observe( 334 NotificationType type, 335 const NotificationSource& source, 336 const NotificationDetails& details) { 337 #if defined(OS_CHROMEOS) 338 if (type == NotificationType::LOGIN_USER_CHANGED) { 339 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 340 if (!command_line.HasSwitch(switches::kTestType)) { 341 // This will fail when running on non cros os. 342 // TODO(davemoore) Need to mock this enough to enable testing. 343 CHECK(chromeos::CrosLibrary::Get()->EnsureLoaded()); 344 // If we don't have a mounted profile directory we're in trouble. 345 // TODO(davemoore) Once we have better api this check should ensure that 346 // our profile directory is the one that's mounted, and that it's mounted 347 // as the current user. 348 CHECK(chromeos::CrosLibrary::Get()->GetCryptohomeLibrary()->IsMounted()); 349 } 350 logged_in_ = true; 351 } 352 #endif 353 } 354 355 void ProfileManager::DoFinalInit(Profile* profile) { 356 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 357 bool init_extensions = true; 358 #if defined(OS_CHROMEOS) 359 if (!logged_in_ && 360 (!command_line.HasSwitch(switches::kTestType) || 361 command_line.HasSwitch(switches::kLoginProfile))) { 362 init_extensions = false; 363 } 364 #endif 365 profile->InitExtensions(init_extensions); 366 367 if (!command_line.HasSwitch(switches::kDisableWebResources)) 368 profile->InitPromoResources(); 369 } 370 371 void ProfileManager::OnProfileCreated(Profile* profile, bool success) { 372 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 373 374 ProfilesInfoMap::iterator iter = profiles_info_.find(profile->GetPath()); 375 DCHECK(iter != profiles_info_.end()); 376 ProfileInfo* info = iter->second.get(); 377 378 std::vector<Observer*> observers; 379 info->observers.swap(observers); 380 381 if (success) { 382 DoFinalInit(profile); 383 info->created = true; 384 #if defined(OS_CHROMEOS) 385 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 386 if (!logged_in_ && 387 (!command_line.HasSwitch(switches::kTestType) || 388 command_line.HasSwitch(switches::kLoginProfile))) { 389 profile = profile->GetOffTheRecordProfile(); 390 } 391 #endif 392 } else { 393 profile = NULL; 394 profiles_info_.erase(iter); 395 } 396 397 for (size_t i = 0; i < observers.size(); ++i) { 398 observers[i]->OnProfileCreated(profile); 399 } 400 } 401