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
SuspendURLRequestJobs()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
SuspendRequestContext(net::URLRequestContextGetter * request_context_getter)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
ResumeRequestContext(net::URLRequestContextGetter * request_context_getter)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
ShutdownSessionServices()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
GetDefaultProfile()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
ProfileManager()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
~ProfileManager()94 ProfileManager::~ProfileManager() {
95 ui::SystemMonitor* system_monitor = ui::SystemMonitor::Get();
96 if (system_monitor)
97 system_monitor->RemoveObserver(this);
98 }
99
GetDefaultProfileDir(const FilePath & user_data_dir)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
GetProfilePrefsPath(const FilePath & profile_dir)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
GetCurrentProfileDir()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
GetDefaultProfile(const FilePath & user_data_dir)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
GetProfileWithId(ProfileId profile_id)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
IsValidProfile(Profile * profile)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
GetLoadedProfiles() const198 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
GetProfile(const FilePath & profile_dir)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
CreateProfileAsync(const FilePath & user_data_dir,Observer * observer)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
CreateDefaultProfileAsync(Observer * observer)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
AddProfile(Profile * profile)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
RegisterProfile(Profile * profile,bool created)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
GetProfileByPath(const FilePath & path) const285 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
OnSuspend()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
OnResume()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
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)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
DoFinalInit(Profile * profile)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
OnProfileCreated(Profile * profile,bool success)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