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
35namespace {
36
37void 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
45void 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
55void 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
67void 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
77Profile* 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
84ProfileManager::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
94ProfileManager::~ProfileManager() {
95  ui::SystemMonitor* system_monitor = ui::SystemMonitor::Get();
96  if (system_monitor)
97    system_monitor->RemoveObserver(this);
98}
99
100FilePath 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
108FilePath 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
115FilePath 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
138Profile* 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
165Profile* 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
183bool 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
198std::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
208Profile* 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
224void 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
247void 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
260bool 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
277ProfileManager::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
285Profile* 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
290void 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
314void 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
333void 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
355void 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
371void 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