Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / startup / startup_browser_creator.cc
1 // Copyright (c) 2012 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/startup/startup_browser_creator.h"
6
7 #include <algorithm>   // For max().
8 #include <set>
9
10 #include "apps/app_load_service.h"
11 #include "apps/switches.h"
12 #include "base/bind.h"
13 #include "base/bind_helpers.h"
14 #include "base/command_line.h"
15 #include "base/compiler_specific.h"
16 #include "base/environment.h"
17 #include "base/file_util.h"
18 #include "base/files/file_path.h"
19 #include "base/lazy_instance.h"
20 #include "base/logging.h"
21 #include "base/memory/scoped_ptr.h"
22 #include "base/metrics/histogram.h"
23 #include "base/metrics/statistics_recorder.h"
24 #include "base/path_service.h"
25 #include "base/prefs/pref_service.h"
26 #include "base/strings/string_number_conversions.h"
27 #include "base/strings/string_split.h"
28 #include "base/strings/utf_string_conversions.h"
29 #include "base/threading/thread_restrictions.h"
30 #include "chrome/browser/app_mode/app_mode_utils.h"
31 #include "chrome/browser/auto_launch_trial.h"
32 #include "chrome/browser/browser_process.h"
33 #include "chrome/browser/chrome_notification_types.h"
34 #include "chrome/browser/custom_handlers/protocol_handler_registry.h"
35 #include "chrome/browser/extensions/startup_helper.h"
36 #include "chrome/browser/extensions/unpacked_installer.h"
37 #include "chrome/browser/first_run/first_run.h"
38 #include "chrome/browser/notifications/desktop_notification_service.h"
39 #include "chrome/browser/prefs/incognito_mode_prefs.h"
40 #include "chrome/browser/prefs/session_startup_pref.h"
41 #include "chrome/browser/profiles/profile.h"
42 #include "chrome/browser/profiles/profile_manager.h"
43 #include "chrome/browser/profiles/profiles_state.h"
44 #include "chrome/browser/search_engines/template_url_service_factory.h"
45 #include "chrome/browser/ui/app_list/app_list_service.h"
46 #include "chrome/browser/ui/browser.h"
47 #include "chrome/browser/ui/browser_dialogs.h"
48 #include "chrome/browser/ui/browser_finder.h"
49 #include "chrome/browser/ui/browser_window.h"
50 #include "chrome/browser/ui/startup/startup_browser_creator_impl.h"
51 #include "chrome/common/chrome_constants.h"
52 #include "chrome/common/chrome_paths.h"
53 #include "chrome/common/chrome_result_codes.h"
54 #include "chrome/common/chrome_switches.h"
55 #include "chrome/common/chrome_version_info.h"
56 #include "chrome/common/pref_names.h"
57 #include "chrome/common/url_constants.h"
58 #include "chrome/installer/util/browser_distribution.h"
59 #include "components/google/core/browser/google_util.h"
60 #include "components/search_engines/util.h"
61 #include "components/signin/core/common/profile_management_switches.h"
62 #include "components/url_fixer/url_fixer.h"
63 #include "content/public/browser/browser_thread.h"
64 #include "content/public/browser/child_process_security_policy.h"
65 #include "content/public/browser/navigation_controller.h"
66 #include "net/base/net_util.h"
67
68 #if defined(USE_ASH)
69 #include "ash/shell.h"
70 #endif
71
72 #if defined(OS_CHROMEOS)
73 #include "chrome/browser/chromeos/app_mode/app_launch_utils.h"
74 #include "chrome/browser/chromeos/kiosk_mode/kiosk_mode_settings.h"
75 #include "chrome/browser/chromeos/login/demo_mode/demo_app_launcher.h"
76 #include "chrome/browser/chromeos/profiles/profile_helper.h"
77 #include "chrome/browser/lifetime/application_lifetime.h"
78 #include "chromeos/chromeos_switches.h"
79 #include "components/user_manager/user_manager.h"
80 #endif
81
82 #if defined(TOOLKIT_VIEWS) && defined(OS_LINUX)
83 #include "ui/events/x/touch_factory_x11.h"
84 #endif
85
86 #if defined(OS_MACOSX)
87 #include "chrome/browser/web_applications/web_app_mac.h"
88 #endif
89
90 #if defined(ENABLE_FULL_PRINTING)
91 #include "chrome/browser/printing/cloud_print/cloud_print_proxy_service.h"
92 #include "chrome/browser/printing/cloud_print/cloud_print_proxy_service_factory.h"
93 #include "chrome/browser/printing/print_dialog_cloud.h"
94 #endif
95
96 using content::BrowserThread;
97 using content::ChildProcessSecurityPolicy;
98
99 namespace {
100
101 // Keeps track on which profiles have been launched.
102 class ProfileLaunchObserver : public content::NotificationObserver {
103  public:
104   ProfileLaunchObserver()
105       : profile_to_activate_(NULL),
106         activated_profile_(false) {
107     registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
108                    content::NotificationService::AllSources());
109     registrar_.Add(this, chrome::NOTIFICATION_BROWSER_WINDOW_READY,
110                    content::NotificationService::AllSources());
111   }
112   virtual ~ProfileLaunchObserver() {}
113
114   virtual void Observe(int type,
115                        const content::NotificationSource& source,
116                        const content::NotificationDetails& details) OVERRIDE {
117     switch (type) {
118       case chrome::NOTIFICATION_PROFILE_DESTROYED: {
119         Profile* profile = content::Source<Profile>(source).ptr();
120         launched_profiles_.erase(profile);
121         opened_profiles_.erase(profile);
122         if (profile == profile_to_activate_)
123           profile_to_activate_ = NULL;
124         // If this profile was the last launched one without an opened window,
125         // then we may be ready to activate |profile_to_activate_|.
126         MaybeActivateProfile();
127         break;
128       }
129       case chrome::NOTIFICATION_BROWSER_WINDOW_READY: {
130         Browser* browser = content::Source<Browser>(source).ptr();
131         DCHECK(browser);
132         opened_profiles_.insert(browser->profile());
133         MaybeActivateProfile();
134         break;
135       }
136       default:
137         NOTREACHED();
138     }
139   }
140
141   bool HasBeenLaunched(const Profile* profile) const {
142     return launched_profiles_.find(profile) != launched_profiles_.end();
143   }
144
145   void AddLaunched(Profile* profile) {
146     launched_profiles_.insert(profile);
147     // Since the startup code only executes for browsers launched in
148     // desktop mode, i.e., HOST_DESKTOP_TYPE_NATIVE. Ash should never get here.
149     if (chrome::FindBrowserWithProfile(profile,
150                                        chrome::HOST_DESKTOP_TYPE_NATIVE)) {
151       // A browser may get opened before we get initialized (e.g., in tests),
152       // so we never see the NOTIFICATION_BROWSER_WINDOW_READY for it.
153       opened_profiles_.insert(profile);
154     }
155   }
156
157   void Clear() {
158     launched_profiles_.clear();
159     opened_profiles_.clear();
160   }
161
162   bool activated_profile() { return activated_profile_; }
163
164   void set_profile_to_activate(Profile* profile) {
165     profile_to_activate_ = profile;
166     MaybeActivateProfile();
167   }
168
169  private:
170   void MaybeActivateProfile() {
171     if (!profile_to_activate_)
172       return;
173     // Check that browsers have been opened for all the launched profiles.
174     // Note that browsers opened for profiles that were not added as launched
175     // profiles are simply ignored.
176     std::set<const Profile*>::const_iterator i = launched_profiles_.begin();
177     for (; i != launched_profiles_.end(); ++i) {
178       if (opened_profiles_.find(*i) == opened_profiles_.end())
179         return;
180     }
181     // Asynchronous post to give a chance to the last window to completely
182     // open and activate before trying to activate |profile_to_activate_|.
183     BrowserThread::PostTask(
184         BrowserThread::UI, FROM_HERE,
185         base::Bind(&ProfileLaunchObserver::ActivateProfile,
186                    base::Unretained(this)));
187     // Avoid posting more than once before ActivateProfile gets called.
188     registrar_.Remove(this, chrome::NOTIFICATION_BROWSER_WINDOW_READY,
189                       content::NotificationService::AllSources());
190     registrar_.Remove(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
191                       content::NotificationService::AllSources());
192   }
193
194   void ActivateProfile() {
195     // We need to test again, in case the profile got deleted in the mean time.
196     if (profile_to_activate_) {
197       Browser* browser = chrome::FindBrowserWithProfile(
198           profile_to_activate_, chrome::HOST_DESKTOP_TYPE_NATIVE);
199       // |profile| may never get launched, e.g., if it only had
200       // incognito Windows and one of them was used to exit Chrome.
201       // So it won't have a browser in that case.
202       if (browser)
203         browser->window()->Activate();
204       // No need try to activate this profile again.
205       profile_to_activate_ = NULL;
206     }
207     // Assign true here, even if no browser was actually activated, so that
208     // the test can stop waiting, and fail gracefully when needed.
209     activated_profile_ = true;
210   }
211
212   // These are the profiles that get launched by
213   // StartupBrowserCreator::LaunchBrowser.
214   std::set<const Profile*> launched_profiles_;
215   // These are the profiles for which at least one browser window has been
216   // opened. This is needed to know when it is safe to activate
217   // |profile_to_activate_|, otherwise, new browser windows being opened will
218   // be activated on top of it.
219   std::set<const Profile*> opened_profiles_;
220   content::NotificationRegistrar registrar_;
221   // This is NULL until the profile to activate has been chosen. This value,
222   // should only be set once all profiles have been launched, otherwise,
223   // activation may not happen after the launch of newer profiles.
224   Profile* profile_to_activate_;
225   // Set once we attempted to activate a profile. We only get one shot at this.
226   bool activated_profile_;
227
228   DISALLOW_COPY_AND_ASSIGN(ProfileLaunchObserver);
229 };
230
231 base::LazyInstance<ProfileLaunchObserver> profile_launch_observer =
232     LAZY_INSTANCE_INITIALIZER;
233
234 // Dumps the current set of the browser process's histograms to |output_file|.
235 // The file is overwritten if it exists. This function should only be called in
236 // the blocking pool.
237 void DumpBrowserHistograms(const base::FilePath& output_file) {
238   base::ThreadRestrictions::AssertIOAllowed();
239
240   std::string output_string(base::StatisticsRecorder::ToJSON(std::string()));
241   base::WriteFile(output_file, output_string.data(),
242                   static_cast<int>(output_string.size()));
243 }
244
245 }  // namespace
246
247 StartupBrowserCreator::StartupBrowserCreator()
248     : is_default_browser_dialog_suppressed_(false),
249       show_main_browser_window_(true) {
250 }
251
252 StartupBrowserCreator::~StartupBrowserCreator() {}
253
254 // static
255 bool StartupBrowserCreator::was_restarted_read_ = false;
256
257 // static
258 bool StartupBrowserCreator::in_synchronous_profile_launch_ = false;
259
260 void StartupBrowserCreator::AddFirstRunTab(const GURL& url) {
261   first_run_tabs_.push_back(url);
262 }
263
264 // static
265 bool StartupBrowserCreator::InSynchronousProfileLaunch() {
266   return in_synchronous_profile_launch_;
267 }
268
269 bool StartupBrowserCreator::LaunchBrowser(
270     const CommandLine& command_line,
271     Profile* profile,
272     const base::FilePath& cur_dir,
273     chrome::startup::IsProcessStartup process_startup,
274     chrome::startup::IsFirstRun is_first_run,
275     int* return_code) {
276
277   in_synchronous_profile_launch_ =
278       process_startup == chrome::startup::IS_PROCESS_STARTUP;
279   DCHECK(profile);
280
281   // Continue with the incognito profile from here on if Incognito mode
282   // is forced.
283   if (IncognitoModePrefs::ShouldLaunchIncognito(command_line,
284                                                 profile->GetPrefs())) {
285     profile = profile->GetOffTheRecordProfile();
286   } else if (command_line.HasSwitch(switches::kIncognito)) {
287     LOG(WARNING) << "Incognito mode disabled by policy, launching a normal "
288                  << "browser session.";
289   }
290
291   // Note: This check should have been done in ProcessCmdLineImpl()
292   // before calling this function. However chromeos/login/login_utils.cc
293   // calls this function directly (see comments there) so it has to be checked
294   // again.
295   const bool silent_launch = command_line.HasSwitch(switches::kSilentLaunch);
296
297   if (!silent_launch) {
298     StartupBrowserCreatorImpl lwp(cur_dir, command_line, this, is_first_run);
299     const std::vector<GURL> urls_to_launch =
300         GetURLsFromCommandLine(command_line, cur_dir, profile);
301     chrome::HostDesktopType host_desktop_type =
302         chrome::HOST_DESKTOP_TYPE_NATIVE;
303
304 #if defined(USE_ASH) && !defined(OS_CHROMEOS)
305     // We want to maintain only one type of instance for now, either ASH
306     // or desktop.
307     // TODO(shrikant): Remove this code once we decide on running both desktop
308     // and ASH instances side by side.
309     if (ash::Shell::HasInstance())
310       host_desktop_type = chrome::HOST_DESKTOP_TYPE_ASH;
311 #endif
312
313     const bool launched = lwp.Launch(profile, urls_to_launch,
314                                in_synchronous_profile_launch_,
315                                host_desktop_type);
316     in_synchronous_profile_launch_ = false;
317     if (!launched) {
318       LOG(ERROR) << "launch error";
319       if (return_code)
320         *return_code = chrome::RESULT_CODE_INVALID_CMDLINE_URL;
321       return false;
322     }
323   } else {
324     in_synchronous_profile_launch_ = false;
325   }
326
327   profile_launch_observer.Get().AddLaunched(profile);
328
329 #if defined(OS_CHROMEOS)
330   chromeos::ProfileHelper::Get()->ProfileStartup(profile, process_startup);
331 #endif
332   return true;
333 }
334
335 // static
336 bool StartupBrowserCreator::WasRestarted() {
337   // Stores the value of the preference kWasRestarted had when it was read.
338   static bool was_restarted = false;
339
340   if (!was_restarted_read_) {
341     PrefService* pref_service = g_browser_process->local_state();
342     was_restarted = pref_service->GetBoolean(prefs::kWasRestarted);
343     pref_service->SetBoolean(prefs::kWasRestarted, false);
344     was_restarted_read_ = true;
345   }
346   return was_restarted;
347 }
348
349 // static
350 SessionStartupPref StartupBrowserCreator::GetSessionStartupPref(
351     const CommandLine& command_line,
352     Profile* profile) {
353   DCHECK(profile);
354   PrefService* prefs = profile->GetPrefs();
355   SessionStartupPref pref = SessionStartupPref::GetStartupPref(prefs);
356
357   // IsChromeFirstRun() looks for a sentinel file to determine whether the user
358   // is starting Chrome for the first time. On Chrome OS, the sentinel is stored
359   // in a location shared by all users and the check is meaningless. Query the
360   // UserManager instead to determine whether the user is new.
361 #if defined(OS_CHROMEOS)
362   const bool is_first_run =
363       user_manager::UserManager::Get()->IsCurrentUserNew();
364 #else
365   const bool is_first_run = first_run::IsChromeFirstRun();
366 #endif
367
368   // The pref has an OS-dependent default value. For the first run only, this
369   // default is overridden with SessionStartupPref::DEFAULT so that first run
370   // behavior (sync promo, welcome page) is consistently invoked.
371   // This applies only if the pref is still at its default and has not been
372   // set by the user, managed prefs or policy.
373   if (is_first_run && SessionStartupPref::TypeIsDefault(prefs))
374     pref.type = SessionStartupPref::DEFAULT;
375
376   // The switches::kRestoreLastSession command line switch is used to restore
377   // sessions after a browser self restart (e.g. after a Chrome upgrade).
378   // However, new profiles can be created from a browser process that has this
379   // switch so do not set the session pref to SessionStartupPref::LAST for
380   // those as there is nothing to restore.
381   if ((command_line.HasSwitch(switches::kRestoreLastSession) ||
382        StartupBrowserCreator::WasRestarted()) &&
383       !profile->IsNewProfile()) {
384     pref.type = SessionStartupPref::LAST;
385   }
386   if (pref.type == SessionStartupPref::LAST &&
387       IncognitoModePrefs::ShouldLaunchIncognito(command_line, prefs)) {
388     // We don't store session information when incognito. If the user has
389     // chosen to restore last session and launched incognito, fallback to
390     // default launch behavior.
391     pref.type = SessionStartupPref::DEFAULT;
392   }
393
394   return pref;
395 }
396
397 // static
398 void StartupBrowserCreator::ClearLaunchedProfilesForTesting() {
399   profile_launch_observer.Get().Clear();
400 }
401
402 // static
403 std::vector<GURL> StartupBrowserCreator::GetURLsFromCommandLine(
404     const CommandLine& command_line,
405     const base::FilePath& cur_dir,
406     Profile* profile) {
407   std::vector<GURL> urls;
408
409   const CommandLine::StringVector& params = command_line.GetArgs();
410   for (size_t i = 0; i < params.size(); ++i) {
411     base::FilePath param = base::FilePath(params[i]);
412     // Handle Vista way of searching - "? <search-term>"
413     if ((param.value().size() > 2) && (param.value()[0] == '?') &&
414         (param.value()[1] == ' ')) {
415       GURL url(GetDefaultSearchURLForSearchTerms(
416           TemplateURLServiceFactory::GetForProfile(profile),
417           param.LossyDisplayName().substr(2)));
418       if (url.is_valid()) {
419         urls.push_back(url);
420         continue;
421       }
422     }
423
424     // Otherwise, fall through to treating it as a URL.
425
426     // This will create a file URL or a regular URL.
427     // This call can (in rare circumstances) block the UI thread.
428     // Allow it until this bug is fixed.
429     //  http://code.google.com/p/chromium/issues/detail?id=60641
430     GURL url = GURL(param.MaybeAsASCII());
431     // http://crbug.com/371030: Only use URLFixerUpper if we don't have a valid
432     // URL, otherwise we will look in the current directory for a file named
433     // 'about' if the browser was started with a about:foo argument.
434     if (!url.is_valid()) {
435       base::ThreadRestrictions::ScopedAllowIO allow_io;
436       url = url_fixer::FixupRelativeFile(cur_dir, param);
437     }
438     // Exclude dangerous schemes.
439     if (url.is_valid()) {
440       ChildProcessSecurityPolicy* policy =
441           ChildProcessSecurityPolicy::GetInstance();
442       if (policy->IsWebSafeScheme(url.scheme()) ||
443           url.SchemeIs(url::kFileScheme) ||
444 #if defined(OS_CHROMEOS)
445           // In ChromeOS, allow any settings page to be specified on the command
446           // line. See ExistingUserController::OnLoginSuccess.
447           (url.spec().find(chrome::kChromeUISettingsURL) == 0) ||
448 #else
449           ((url.spec().find(std::string(chrome::kChromeUISettingsURL) +
450                             chrome::kResetProfileSettingsSubPage) == 0)) ||
451 #endif
452           (url.spec().compare(url::kAboutBlankURL) == 0)) {
453         urls.push_back(url);
454       }
455     }
456   }
457   return urls;
458 }
459
460 // static
461 bool StartupBrowserCreator::ProcessCmdLineImpl(
462     const CommandLine& command_line,
463     const base::FilePath& cur_dir,
464     bool process_startup,
465     Profile* last_used_profile,
466     const Profiles& last_opened_profiles,
467     int* return_code,
468     StartupBrowserCreator* browser_creator) {
469   DCHECK(last_used_profile);
470   if (process_startup) {
471     if (command_line.HasSwitch(switches::kDisablePromptOnRepost))
472       content::NavigationController::DisablePromptOnRepost();
473   }
474
475   bool silent_launch = false;
476
477 #if defined(ENABLE_FULL_PRINTING)
478   // If we are just displaying a print dialog we shouldn't open browser
479   // windows.
480   if (command_line.HasSwitch(switches::kCloudPrintFile) &&
481       print_dialog_cloud::CreatePrintDialogFromCommandLine(last_used_profile,
482                                                            command_line)) {
483     silent_launch = true;
484   }
485
486   // If we are checking the proxy enabled policy, don't open any windows.
487   if (command_line.HasSwitch(switches::kCheckCloudPrintConnectorPolicy)) {
488     silent_launch = true;
489     if (CloudPrintProxyServiceFactory::GetForProfile(last_used_profile)->
490         EnforceCloudPrintConnectorPolicyAndQuit())
491       // Success, nothing more needs to be done, so return false to stop
492       // launching and quit.
493       return false;
494   }
495 #endif  // defined(ENABLE_FULL_PRINTING)
496
497   if (command_line.HasSwitch(switches::kExplicitlyAllowedPorts)) {
498     std::string allowed_ports =
499         command_line.GetSwitchValueASCII(switches::kExplicitlyAllowedPorts);
500     net::SetExplicitlyAllowedPorts(allowed_ports);
501   }
502
503   if (command_line.HasSwitch(switches::kInstallFromWebstore)) {
504     extensions::StartupHelper helper;
505     helper.InstallFromWebstore(command_line, last_used_profile);
506     // Nothing more needs to be done, so return false to stop launching and
507     // quit.
508     return false;
509   }
510
511   if (command_line.HasSwitch(switches::kValidateCrx)) {
512     if (!process_startup) {
513       LOG(ERROR) << "chrome is already running; you must close all running "
514                  << "instances before running with the --"
515                  << switches::kValidateCrx << " flag";
516       return false;
517     }
518     extensions::StartupHelper helper;
519     std::string message;
520     std::string error;
521     if (helper.ValidateCrx(command_line, &error))
522       message = std::string("ValidateCrx Success");
523     else
524       message = std::string("ValidateCrx Failure: ") + error;
525     printf("%s\n", message.c_str());
526     return false;
527   }
528
529   if (command_line.HasSwitch(switches::kLimitedInstallFromWebstore)) {
530     extensions::StartupHelper helper;
531     helper.LimitedInstallFromWebstore(command_line, last_used_profile,
532                                       base::Bind(&base::DoNothing));
533   }
534
535 #if defined(OS_CHROMEOS)
536   // The browser will be launched after the user logs in.
537   if (command_line.HasSwitch(chromeos::switches::kLoginManager))
538     silent_launch = true;
539
540   if (chrome::IsRunningInAppMode() &&
541       command_line.HasSwitch(switches::kAppId)) {
542     chromeos::LaunchAppOrDie(
543         last_used_profile,
544         command_line.GetSwitchValueASCII(switches::kAppId));
545
546     // Skip browser launch since app mode launches its app window.
547     silent_launch = true;
548   }
549
550   // If we are a demo app session and we crashed, there is no safe recovery
551   // possible. We should instead cleanly exit and go back to the OOBE screen,
552   // where we will launch again after the timeout has expired.
553   if (chromeos::DemoAppLauncher::IsDemoAppSession(
554       command_line.GetSwitchValueASCII(chromeos::switches::kLoginUser))) {
555     chrome::AttemptUserExit();
556     return false;
557   }
558 #endif
559
560 #if defined(TOOLKIT_VIEWS) && defined(USE_X11)
561   ui::TouchFactory::SetTouchDeviceListFromCommandLine();
562 #endif
563
564 #if defined(OS_MACOSX)
565   if (web_app::MaybeRebuildShortcut(command_line))
566     return true;
567 #endif
568
569   if (!process_startup &&
570       command_line.HasSwitch(switches::kDumpBrowserHistograms)) {
571     // Only handle --dump-browser-histograms from a rendezvous. In this case, do
572     // not open a new browser window even if no output file was given.
573     base::FilePath output_file(
574         command_line.GetSwitchValuePath(switches::kDumpBrowserHistograms));
575     if (!output_file.empty()) {
576       BrowserThread::PostBlockingPoolTask(
577           FROM_HERE,
578           base::Bind(&DumpBrowserHistograms, output_file));
579     }
580     silent_launch = true;
581   }
582
583   // If we don't want to launch a new browser window or tab we are done here.
584   if (silent_launch)
585     return true;
586
587   // Check for --load-and-launch-app.
588   if (command_line.HasSwitch(apps::kLoadAndLaunchApp) &&
589       !IncognitoModePrefs::ShouldLaunchIncognito(
590           command_line, last_used_profile->GetPrefs())) {
591     CommandLine::StringType path = command_line.GetSwitchValueNative(
592         apps::kLoadAndLaunchApp);
593
594     if (!apps::AppLoadService::Get(last_used_profile)->LoadAndLaunch(
595             base::FilePath(path), command_line, cur_dir)) {
596       return false;
597     }
598
599     // Return early here since we don't want to open a browser window.
600     // The exception is when there are no browser windows, since we don't want
601     // chrome to shut down.
602     // TODO(jackhou): Do this properly once keep-alive is handled by the
603     // background page of apps. Tracked at http://crbug.com/175381
604     if (chrome::GetTotalBrowserCountForProfile(last_used_profile) != 0)
605       return true;
606   }
607
608   chrome::startup::IsProcessStartup is_process_startup = process_startup ?
609       chrome::startup::IS_PROCESS_STARTUP :
610       chrome::startup::IS_NOT_PROCESS_STARTUP;
611   chrome::startup::IsFirstRun is_first_run = first_run::IsChromeFirstRun() ?
612       chrome::startup::IS_FIRST_RUN : chrome::startup::IS_NOT_FIRST_RUN;
613   // |last_opened_profiles| will be empty in the following circumstances:
614   // - This is the first launch. |last_used_profile| is the initial profile.
615   // - The user exited the browser by closing all windows for all
616   // profiles. |last_used_profile| is the profile which owned the last open
617   // window.
618   // - Only incognito windows were open when the browser exited.
619   // |last_used_profile| is the last used incognito profile. Restoring it will
620   // create a browser window for the corresponding original profile.
621   if (last_opened_profiles.empty()) {
622     // If the last used profile is locked or was a guest, show the user manager.
623     if (switches::IsNewAvatarMenu()) {
624       ProfileInfoCache& profile_info =
625           g_browser_process->profile_manager()->GetProfileInfoCache();
626       size_t profile_index = profile_info.GetIndexOfProfileWithPath(
627           last_used_profile->GetPath());
628       bool signin_required = profile_index != std::string::npos &&
629           profile_info.ProfileIsSigninRequiredAtIndex(profile_index);
630       if (signin_required || last_used_profile->IsGuestSession()) {
631         chrome::ShowUserManager(base::FilePath());
632         return true;
633       }
634     }
635     if (!browser_creator->LaunchBrowser(command_line, last_used_profile,
636                                         cur_dir, is_process_startup,
637                                         is_first_run, return_code)) {
638       return false;
639     }
640   } else {
641     // Launch the last used profile with the full command line, and the other
642     // opened profiles without the URLs to launch.
643     CommandLine command_line_without_urls(command_line.GetProgram());
644     const CommandLine::SwitchMap& switches = command_line.GetSwitches();
645     for (CommandLine::SwitchMap::const_iterator switch_it = switches.begin();
646          switch_it != switches.end(); ++switch_it) {
647       command_line_without_urls.AppendSwitchNative(switch_it->first,
648                                                    switch_it->second);
649     }
650     // Launch the profiles in the order they became active.
651     for (Profiles::const_iterator it = last_opened_profiles.begin();
652          it != last_opened_profiles.end(); ++it) {
653       // Don't launch additional profiles which would only open a new tab
654       // page. When restarting after an update, all profiles will reopen last
655       // open pages.
656       SessionStartupPref startup_pref =
657           GetSessionStartupPref(command_line, *it);
658       if (*it != last_used_profile &&
659           startup_pref.type == SessionStartupPref::DEFAULT &&
660           !HasPendingUncleanExit(*it))
661         continue;
662
663       // Don't re-open a browser window for the guest profile.
664       if (switches::IsNewAvatarMenu() &&
665           (*it)->IsGuestSession())
666         continue;
667
668       if (!browser_creator->LaunchBrowser((*it == last_used_profile) ?
669           command_line : command_line_without_urls, *it, cur_dir,
670           is_process_startup, is_first_run, return_code))
671         return false;
672       // We've launched at least one browser.
673       is_process_startup = chrome::startup::IS_NOT_PROCESS_STARTUP;
674     }
675     // This must be done after all profiles have been launched so the observer
676     // knows about all profiles to wait for before activating this one.
677
678     // If the last used profile was the guest one, we didn't open it so
679     // we don't need to activate it either.
680     if (!switches::IsNewAvatarMenu() &&
681         !last_used_profile->IsGuestSession())
682       profile_launch_observer.Get().set_profile_to_activate(last_used_profile);
683   }
684   return true;
685 }
686
687 // static
688 void StartupBrowserCreator::ProcessCommandLineOnProfileCreated(
689     const CommandLine& command_line,
690     const base::FilePath& cur_dir,
691     Profile* profile,
692     Profile::CreateStatus status) {
693   if (status == Profile::CREATE_STATUS_INITIALIZED)
694     ProcessCmdLineImpl(command_line, cur_dir, false, profile, Profiles(), NULL,
695                        NULL);
696 }
697
698 // static
699 void StartupBrowserCreator::ProcessCommandLineAlreadyRunning(
700     const CommandLine& command_line,
701     const base::FilePath& cur_dir,
702     const base::FilePath& profile_path) {
703   ProfileManager* profile_manager = g_browser_process->profile_manager();
704   Profile* profile = profile_manager->GetProfileByPath(profile_path);
705
706   // The profile isn't loaded yet and so needs to be loaded asynchronously.
707   if (!profile) {
708     profile_manager->CreateProfileAsync(profile_path,
709         base::Bind(&StartupBrowserCreator::ProcessCommandLineOnProfileCreated,
710                    command_line, cur_dir), base::string16(), base::string16(),
711                    std::string());
712     return;
713   }
714
715   ProcessCmdLineImpl(command_line, cur_dir, false, profile, Profiles(), NULL,
716                      NULL);
717 }
718
719 // static
720 bool StartupBrowserCreator::ActivatedProfile() {
721   return profile_launch_observer.Get().activated_profile();
722 }
723
724 bool HasPendingUncleanExit(Profile* profile) {
725   return profile->GetLastSessionExitType() == Profile::EXIT_CRASHED &&
726       !profile_launch_observer.Get().HasBeenLaunched(profile);
727 }
728
729 base::FilePath GetStartupProfilePath(const base::FilePath& user_data_dir,
730                                      const CommandLine& command_line) {
731   // If we are showing the app list then chrome isn't shown so load the app
732   // list's profile rather than chrome's.
733   if (command_line.HasSwitch(switches::kShowAppList)) {
734     return AppListService::Get(chrome::HOST_DESKTOP_TYPE_NATIVE)->
735         GetProfilePath(user_data_dir);
736   }
737
738   if (command_line.HasSwitch(switches::kProfileDirectory)) {
739     return user_data_dir.Append(
740         command_line.GetSwitchValuePath(switches::kProfileDirectory));
741   }
742
743   return g_browser_process->profile_manager()->GetLastUsedProfileDir(
744       user_data_dir);
745 }