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.
5 #include "chrome/browser/ui/startup/startup_browser_creator.h"
7 #include <algorithm> // For max().
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/files/file_path.h"
18 #include "base/files/file_util.h"
19 #include "base/lazy_instance.h"
20 #include "base/logging.h"
21 #include "base/memory/scoped_ptr.h"
22 #include "base/metrics/histogram_macros.h"
23 #include "base/metrics/statistics_recorder.h"
24 #include "base/prefs/pref_service.h"
25 #include "base/profiler/scoped_profile.h"
26 #include "base/strings/string_number_conversions.h"
27 #include "base/strings/string_split.h"
28 #include "base/strings/string_tokenizer.h"
29 #include "base/strings/utf_string_conversions.h"
30 #include "base/threading/thread_restrictions.h"
31 #include "base/trace_event/trace_event.h"
32 #include "chrome/browser/app_mode/app_mode_utils.h"
33 #include "chrome/browser/auto_launch_trial.h"
34 #include "chrome/browser/browser_process.h"
35 #include "chrome/browser/chrome_notification_types.h"
36 #include "chrome/browser/custom_handlers/protocol_handler_registry.h"
37 #include "chrome/browser/extensions/startup_helper.h"
38 #include "chrome/browser/extensions/unpacked_installer.h"
39 #include "chrome/browser/first_run/first_run.h"
40 #include "chrome/browser/notifications/desktop_notification_service.h"
41 #include "chrome/browser/prefs/incognito_mode_prefs.h"
42 #include "chrome/browser/prefs/session_startup_pref.h"
43 #include "chrome/browser/profiles/profile.h"
44 #include "chrome/browser/profiles/profile_manager.h"
45 #include "chrome/browser/profiles/profiles_state.h"
46 #include "chrome/browser/search_engines/template_url_service_factory.h"
47 #include "chrome/browser/ui/app_list/app_list_service.h"
48 #include "chrome/browser/ui/browser.h"
49 #include "chrome/browser/ui/browser_dialogs.h"
50 #include "chrome/browser/ui/browser_finder.h"
51 #include "chrome/browser/ui/browser_window.h"
52 #include "chrome/browser/ui/startup/startup_browser_creator_impl.h"
53 #include "chrome/browser/ui/user_manager.h"
54 #include "chrome/common/chrome_constants.h"
55 #include "chrome/common/chrome_paths.h"
56 #include "chrome/common/chrome_result_codes.h"
57 #include "chrome/common/chrome_switches.h"
58 #include "chrome/common/chrome_version_info.h"
59 #include "chrome/common/pref_names.h"
60 #include "chrome/common/url_constants.h"
61 #include "chrome/installer/util/browser_distribution.h"
62 #include "components/google/core/browser/google_util.h"
63 #include "components/search_engines/util.h"
64 #include "components/signin/core/common/profile_management_switches.h"
65 #include "components/url_fixer/url_fixer.h"
66 #include "content/public/browser/browser_thread.h"
67 #include "content/public/browser/child_process_security_policy.h"
68 #include "content/public/browser/navigation_controller.h"
69 #include "content/public/common/content_switches.h"
70 #include "extensions/common/switches.h"
71 #include "net/base/net_util.h"
74 #include "ash/shell.h"
77 #if defined(OS_CHROMEOS)
78 #include "chrome/browser/chromeos/app_mode/app_launch_utils.h"
79 #include "chrome/browser/chromeos/login/demo_mode/demo_app_launcher.h"
80 #include "chrome/browser/chromeos/profiles/profile_helper.h"
81 #include "chrome/browser/lifetime/application_lifetime.h"
82 #include "chromeos/chromeos_switches.h"
83 #include "components/user_manager/user_manager.h"
86 #if defined(TOOLKIT_VIEWS) && defined(OS_LINUX)
87 #include "ui/events/devices/x11/touch_factory_x11.h"
90 #if defined(OS_MACOSX)
91 #include "chrome/browser/web_applications/web_app_mac.h"
95 #include "chrome/browser/metrics/jumplist_metrics_win.h"
98 #if defined(ENABLE_PRINT_PREVIEW)
99 #include "chrome/browser/printing/cloud_print/cloud_print_proxy_service.h"
100 #include "chrome/browser/printing/cloud_print/cloud_print_proxy_service_factory.h"
101 #include "chrome/browser/printing/print_dialog_cloud.h"
104 using content::BrowserThread
;
105 using content::ChildProcessSecurityPolicy
;
109 // Keeps track on which profiles have been launched.
110 class ProfileLaunchObserver
: public content::NotificationObserver
{
112 ProfileLaunchObserver()
113 : profile_to_activate_(NULL
),
114 activated_profile_(false) {
115 registrar_
.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED
,
116 content::NotificationService::AllSources());
117 registrar_
.Add(this, chrome::NOTIFICATION_BROWSER_WINDOW_READY
,
118 content::NotificationService::AllSources());
120 ~ProfileLaunchObserver() override
{}
122 void Observe(int type
,
123 const content::NotificationSource
& source
,
124 const content::NotificationDetails
& details
) override
{
126 case chrome::NOTIFICATION_PROFILE_DESTROYED
: {
127 Profile
* profile
= content::Source
<Profile
>(source
).ptr();
128 launched_profiles_
.erase(profile
);
129 opened_profiles_
.erase(profile
);
130 if (profile
== profile_to_activate_
)
131 profile_to_activate_
= NULL
;
132 // If this profile was the last launched one without an opened window,
133 // then we may be ready to activate |profile_to_activate_|.
134 MaybeActivateProfile();
137 case chrome::NOTIFICATION_BROWSER_WINDOW_READY
: {
138 Browser
* browser
= content::Source
<Browser
>(source
).ptr();
140 opened_profiles_
.insert(browser
->profile());
141 MaybeActivateProfile();
149 bool HasBeenLaunched(const Profile
* profile
) const {
150 return launched_profiles_
.find(profile
) != launched_profiles_
.end();
153 void AddLaunched(Profile
* profile
) {
154 launched_profiles_
.insert(profile
);
155 // Since the startup code only executes for browsers launched in
156 // desktop mode, i.e., HOST_DESKTOP_TYPE_NATIVE. Ash should never get here.
157 if (chrome::FindBrowserWithProfile(profile
,
158 chrome::HOST_DESKTOP_TYPE_NATIVE
)) {
159 // A browser may get opened before we get initialized (e.g., in tests),
160 // so we never see the NOTIFICATION_BROWSER_WINDOW_READY for it.
161 opened_profiles_
.insert(profile
);
166 launched_profiles_
.clear();
167 opened_profiles_
.clear();
170 bool activated_profile() { return activated_profile_
; }
172 void set_profile_to_activate(Profile
* profile
) {
173 profile_to_activate_
= profile
;
174 MaybeActivateProfile();
178 void MaybeActivateProfile() {
179 if (!profile_to_activate_
)
181 // Check that browsers have been opened for all the launched profiles.
182 // Note that browsers opened for profiles that were not added as launched
183 // profiles are simply ignored.
184 std::set
<const Profile
*>::const_iterator i
= launched_profiles_
.begin();
185 for (; i
!= launched_profiles_
.end(); ++i
) {
186 if (opened_profiles_
.find(*i
) == opened_profiles_
.end())
189 // Asynchronous post to give a chance to the last window to completely
190 // open and activate before trying to activate |profile_to_activate_|.
191 BrowserThread::PostTask(
192 BrowserThread::UI
, FROM_HERE
,
193 base::Bind(&ProfileLaunchObserver::ActivateProfile
,
194 base::Unretained(this)));
195 // Avoid posting more than once before ActivateProfile gets called.
196 registrar_
.Remove(this, chrome::NOTIFICATION_BROWSER_WINDOW_READY
,
197 content::NotificationService::AllSources());
198 registrar_
.Remove(this, chrome::NOTIFICATION_PROFILE_DESTROYED
,
199 content::NotificationService::AllSources());
202 void ActivateProfile() {
203 // We need to test again, in case the profile got deleted in the mean time.
204 if (profile_to_activate_
) {
205 Browser
* browser
= chrome::FindBrowserWithProfile(
206 profile_to_activate_
, chrome::HOST_DESKTOP_TYPE_NATIVE
);
207 // |profile| may never get launched, e.g., if it only had
208 // incognito Windows and one of them was used to exit Chrome.
209 // So it won't have a browser in that case.
211 browser
->window()->Activate();
212 // No need try to activate this profile again.
213 profile_to_activate_
= NULL
;
215 // Assign true here, even if no browser was actually activated, so that
216 // the test can stop waiting, and fail gracefully when needed.
217 activated_profile_
= true;
220 // These are the profiles that get launched by
221 // StartupBrowserCreator::LaunchBrowser.
222 std::set
<const Profile
*> launched_profiles_
;
223 // These are the profiles for which at least one browser window has been
224 // opened. This is needed to know when it is safe to activate
225 // |profile_to_activate_|, otherwise, new browser windows being opened will
226 // be activated on top of it.
227 std::set
<const Profile
*> opened_profiles_
;
228 content::NotificationRegistrar registrar_
;
229 // This is NULL until the profile to activate has been chosen. This value,
230 // should only be set once all profiles have been launched, otherwise,
231 // activation may not happen after the launch of newer profiles.
232 Profile
* profile_to_activate_
;
233 // Set once we attempted to activate a profile. We only get one shot at this.
234 bool activated_profile_
;
236 DISALLOW_COPY_AND_ASSIGN(ProfileLaunchObserver
);
239 base::LazyInstance
<ProfileLaunchObserver
> profile_launch_observer
=
240 LAZY_INSTANCE_INITIALIZER
;
242 // Dumps the current set of the browser process's histograms to |output_file|.
243 // The file is overwritten if it exists. This function should only be called in
244 // the blocking pool.
245 void DumpBrowserHistograms(const base::FilePath
& output_file
) {
246 base::ThreadRestrictions::AssertIOAllowed();
248 std::string
output_string(base::StatisticsRecorder::ToJSON(std::string()));
249 base::WriteFile(output_file
, output_string
.data(),
250 static_cast<int>(output_string
.size()));
255 StartupBrowserCreator::StartupBrowserCreator()
256 : is_default_browser_dialog_suppressed_(false),
257 show_main_browser_window_(true) {
260 StartupBrowserCreator::~StartupBrowserCreator() {}
263 bool StartupBrowserCreator::was_restarted_read_
= false;
266 bool StartupBrowserCreator::in_synchronous_profile_launch_
= false;
268 void StartupBrowserCreator::AddFirstRunTab(const GURL
& url
) {
269 first_run_tabs_
.push_back(url
);
272 bool StartupBrowserCreator::Start(const base::CommandLine
& cmd_line
,
273 const base::FilePath
& cur_dir
,
274 Profile
* last_used_profile
,
275 const Profiles
& last_opened_profiles
) {
276 TRACE_EVENT0("startup", "StartupBrowserCreator::Start");
277 TRACK_SCOPED_REGION("Startup", "StartupBrowserCreator::Start");
278 SCOPED_UMA_HISTOGRAM_TIMER("Startup.StartupBrowserCreator_Start");
279 return ProcessCmdLineImpl(cmd_line
, cur_dir
, true, last_used_profile
,
280 last_opened_profiles
, this);
284 bool StartupBrowserCreator::InSynchronousProfileLaunch() {
285 return in_synchronous_profile_launch_
;
288 bool StartupBrowserCreator::LaunchBrowser(
289 const base::CommandLine
& command_line
,
291 const base::FilePath
& cur_dir
,
292 chrome::startup::IsProcessStartup process_startup
,
293 chrome::startup::IsFirstRun is_first_run
) {
294 in_synchronous_profile_launch_
=
295 process_startup
== chrome::startup::IS_PROCESS_STARTUP
;
298 // Continue with the incognito profile from here on if Incognito mode
300 if (IncognitoModePrefs::ShouldLaunchIncognito(command_line
,
301 profile
->GetPrefs())) {
302 profile
= profile
->GetOffTheRecordProfile();
303 } else if (command_line
.HasSwitch(switches::kIncognito
)) {
304 LOG(WARNING
) << "Incognito mode disabled by policy, launching a normal "
305 << "browser session.";
308 // Note: This check should have been done in ProcessCmdLineImpl()
309 // before calling this function. However chromeos/login/login_utils.cc
310 // calls this function directly (see comments there) so it has to be checked
312 const bool silent_launch
= command_line
.HasSwitch(switches::kSilentLaunch
);
314 if (!silent_launch
) {
315 StartupBrowserCreatorImpl
lwp(cur_dir
, command_line
, this, is_first_run
);
316 const std::vector
<GURL
> urls_to_launch
=
317 GetURLsFromCommandLine(command_line
, cur_dir
, profile
);
318 chrome::HostDesktopType host_desktop_type
=
319 chrome::HOST_DESKTOP_TYPE_NATIVE
;
321 #if defined(USE_ASH) && !defined(OS_CHROMEOS)
322 // We want to maintain only one type of instance for now, either ASH
324 // TODO(shrikant): Remove this code once we decide on running both desktop
325 // and ASH instances side by side.
326 if (ash::Shell::HasInstance())
327 host_desktop_type
= chrome::HOST_DESKTOP_TYPE_ASH
;
330 const bool launched
= lwp
.Launch(profile
, urls_to_launch
,
331 in_synchronous_profile_launch_
,
333 in_synchronous_profile_launch_
= false;
335 LOG(ERROR
) << "launch error";
339 in_synchronous_profile_launch_
= false;
342 profile_launch_observer
.Get().AddLaunched(profile
);
344 #if defined(OS_CHROMEOS)
345 chromeos::ProfileHelper::Get()->ProfileStartup(profile
, process_startup
);
351 bool StartupBrowserCreator::WasRestarted() {
352 // Stores the value of the preference kWasRestarted had when it was read.
353 static bool was_restarted
= false;
355 if (!was_restarted_read_
) {
356 PrefService
* pref_service
= g_browser_process
->local_state();
357 was_restarted
= pref_service
->GetBoolean(prefs::kWasRestarted
);
358 pref_service
->SetBoolean(prefs::kWasRestarted
, false);
359 was_restarted_read_
= true;
361 return was_restarted
;
365 SessionStartupPref
StartupBrowserCreator::GetSessionStartupPref(
366 const base::CommandLine
& command_line
,
369 PrefService
* prefs
= profile
->GetPrefs();
370 SessionStartupPref pref
= SessionStartupPref::GetStartupPref(prefs
);
372 // IsChromeFirstRun() looks for a sentinel file to determine whether the user
373 // is starting Chrome for the first time. On Chrome OS, the sentinel is stored
374 // in a location shared by all users and the check is meaningless. Query the
375 // UserManager instead to determine whether the user is new.
376 #if defined(OS_CHROMEOS)
377 const bool is_first_run
=
378 user_manager::UserManager::Get()->IsCurrentUserNew();
379 // On ChromeOS restarts force the user to login again. The expectation is that
380 // after a login the user gets clean state. For this reason we ignore
381 // StartupBrowserCreator::WasRestarted().
382 const bool did_restart
= false;
384 const bool is_first_run
= first_run::IsChromeFirstRun();
385 const bool did_restart
= StartupBrowserCreator::WasRestarted();
388 // The pref has an OS-dependent default value. For the first run only, this
389 // default is overridden with SessionStartupPref::DEFAULT so that first run
390 // behavior (sync promo, welcome page) is consistently invoked.
391 // This applies only if the pref is still at its default and has not been
392 // set by the user, managed prefs or policy.
393 if (is_first_run
&& SessionStartupPref::TypeIsDefault(prefs
))
394 pref
.type
= SessionStartupPref::DEFAULT
;
396 // The switches::kRestoreLastSession command line switch is used to restore
397 // sessions after a browser self restart (e.g. after a Chrome upgrade).
398 // However, new profiles can be created from a browser process that has this
399 // switch so do not set the session pref to SessionStartupPref::LAST for
400 // those as there is nothing to restore.
401 if ((command_line
.HasSwitch(switches::kRestoreLastSession
) || did_restart
) &&
402 !profile
->IsNewProfile()) {
403 pref
.type
= SessionStartupPref::LAST
;
406 // A browser starting for a profile being unlocked should always restore.
407 if (!profile
->IsGuestSession()) {
408 ProfileInfoCache
& info_cache
=
409 g_browser_process
->profile_manager()->GetProfileInfoCache();
410 size_t index
= info_cache
.GetIndexOfProfileWithPath(profile
->GetPath());
412 if (index
!= std::string::npos
&&
413 info_cache
.ProfileIsSigninRequiredAtIndex(index
)) {
414 pref
.type
= SessionStartupPref::LAST
;
418 if (pref
.type
== SessionStartupPref::LAST
&&
419 IncognitoModePrefs::ShouldLaunchIncognito(command_line
, prefs
)) {
420 // We don't store session information when incognito. If the user has
421 // chosen to restore last session and launched incognito, fallback to
422 // default launch behavior.
423 pref
.type
= SessionStartupPref::DEFAULT
;
430 void StartupBrowserCreator::ClearLaunchedProfilesForTesting() {
431 profile_launch_observer
.Get().Clear();
435 std::vector
<GURL
> StartupBrowserCreator::GetURLsFromCommandLine(
436 const base::CommandLine
& command_line
,
437 const base::FilePath
& cur_dir
,
439 std::vector
<GURL
> urls
;
441 const base::CommandLine::StringVector
& params
= command_line
.GetArgs();
442 for (size_t i
= 0; i
< params
.size(); ++i
) {
443 base::FilePath param
= base::FilePath(params
[i
]);
444 // Handle Vista way of searching - "? <search-term>"
445 if ((param
.value().size() > 2) && (param
.value()[0] == '?') &&
446 (param
.value()[1] == ' ')) {
447 GURL
url(GetDefaultSearchURLForSearchTerms(
448 TemplateURLServiceFactory::GetForProfile(profile
),
449 param
.LossyDisplayName().substr(2)));
450 if (url
.is_valid()) {
456 // Otherwise, fall through to treating it as a URL.
458 // This will create a file URL or a regular URL.
459 // This call can (in rare circumstances) block the UI thread.
460 // Allow it until this bug is fixed.
461 // http://code.google.com/p/chromium/issues/detail?id=60641
462 GURL url
= GURL(param
.MaybeAsASCII());
463 // http://crbug.com/371030: Only use URLFixerUpper if we don't have a valid
464 // URL, otherwise we will look in the current directory for a file named
465 // 'about' if the browser was started with a about:foo argument.
466 if (!url
.is_valid()) {
467 base::ThreadRestrictions::ScopedAllowIO allow_io
;
468 url
= url_fixer::FixupRelativeFile(cur_dir
, param
);
470 // Exclude dangerous schemes.
471 if (url
.is_valid()) {
472 ChildProcessSecurityPolicy
* policy
=
473 ChildProcessSecurityPolicy::GetInstance();
474 if (policy
->IsWebSafeScheme(url
.scheme()) ||
475 url
.SchemeIs(url::kFileScheme
) ||
476 #if defined(OS_CHROMEOS)
477 // In ChromeOS, allow any settings page to be specified on the command
478 // line. See ExistingUserController::OnLoginSuccess.
479 (url
.spec().find(chrome::kChromeUISettingsURL
) == 0) ||
481 ((url
.spec().find(std::string(chrome::kChromeUISettingsURL
) +
482 chrome::kResetProfileSettingsSubPage
) == 0)) ||
484 (url
.spec().compare(url::kAboutBlankURL
) == 0)) {
493 bool StartupBrowserCreator::ProcessCmdLineImpl(
494 const base::CommandLine
& command_line
,
495 const base::FilePath
& cur_dir
,
496 bool process_startup
,
497 Profile
* last_used_profile
,
498 const Profiles
& last_opened_profiles
,
499 StartupBrowserCreator
* browser_creator
) {
500 TRACE_EVENT0("startup", "StartupBrowserCreator::ProcessCmdLineImpl");
502 VLOG(2) << "ProcessCmdLineImpl : BEGIN";
503 DCHECK(last_used_profile
);
504 if (process_startup
) {
505 if (command_line
.HasSwitch(switches::kDisablePromptOnRepost
))
506 content::NavigationController::DisablePromptOnRepost();
509 bool silent_launch
= false;
511 #if defined(ENABLE_PRINT_PREVIEW)
512 // If we are just displaying a print dialog we shouldn't open browser
514 if (command_line
.HasSwitch(switches::kCloudPrintFile
) &&
515 print_dialog_cloud::CreatePrintDialogFromCommandLine(last_used_profile
,
517 silent_launch
= true;
519 #endif // defined(ENABLE_PRINT_PREVIEW)
521 VLOG(2) << "ProcessCmdLineImpl: PLACE 1";
522 if (command_line
.HasSwitch(switches::kExplicitlyAllowedPorts
)) {
523 std::string allowed_ports
=
524 command_line
.GetSwitchValueASCII(switches::kExplicitlyAllowedPorts
);
525 net::SetExplicitlyAllowedPorts(allowed_ports
);
528 if (command_line
.HasSwitch(switches::kInstallEphemeralAppFromWebstore
)) {
529 extensions::StartupHelper helper
;
530 helper
.InstallEphemeralApp(command_line
, last_used_profile
);
531 // Nothing more needs to be done, so return false to stop launching and
536 VLOG(2) << "ProcessCmdLineImpl: PLACE 2";
537 if (command_line
.HasSwitch(switches::kValidateCrx
)) {
538 if (!process_startup
) {
539 LOG(ERROR
) << "chrome is already running; you must close all running "
540 << "instances before running with the --"
541 << switches::kValidateCrx
<< " flag";
544 extensions::StartupHelper helper
;
547 if (helper
.ValidateCrx(command_line
, &error
))
548 message
= std::string("ValidateCrx Success");
550 message
= std::string("ValidateCrx Failure: ") + error
;
551 printf("%s\n", message
.c_str());
555 #if defined(OS_CHROMEOS)
557 // The browser will be launched after the user logs in.
558 if (command_line
.HasSwitch(chromeos::switches::kLoginManager
))
559 silent_launch
= true;
561 if (chrome::IsRunningInAppMode() &&
562 command_line
.HasSwitch(switches::kAppId
)) {
563 chromeos::LaunchAppOrDie(
565 command_line
.GetSwitchValueASCII(switches::kAppId
));
567 // Skip browser launch since app mode launches its app window.
568 silent_launch
= true;
571 // If we are a demo app session and we crashed, there is no safe recovery
572 // possible. We should instead cleanly exit and go back to the OOBE screen,
573 // where we will launch again after the timeout has expired.
574 if (chromeos::DemoAppLauncher::IsDemoAppSession(
575 command_line
.GetSwitchValueASCII(chromeos::switches::kLoginUser
))) {
576 chrome::AttemptUserExit();
579 #endif // OS_CHROMEOS
581 #if defined(TOOLKIT_VIEWS) && defined(USE_X11)
582 ui::TouchFactory::SetTouchDeviceListFromCommandLine();
585 VLOG(2) << "ProcessCmdLineImpl: PLACE 3";
586 #if defined(OS_MACOSX)
587 if (web_app::MaybeRebuildShortcut(command_line
))
591 if (!process_startup
&&
592 command_line
.HasSwitch(switches::kDumpBrowserHistograms
)) {
593 // Only handle --dump-browser-histograms from a rendezvous. In this case, do
594 // not open a new browser window even if no output file was given.
595 base::FilePath
output_file(
596 command_line
.GetSwitchValuePath(switches::kDumpBrowserHistograms
));
597 if (!output_file
.empty()) {
598 BrowserThread::PostBlockingPoolTask(
600 base::Bind(&DumpBrowserHistograms
, output_file
));
602 silent_launch
= true;
605 // If --no-startup-window is specified and Chrome is already running then do
606 // not open a new window.
607 if (!process_startup
&& command_line
.HasSwitch(switches::kNoStartupWindow
))
608 silent_launch
= true;
610 // If we don't want to launch a new browser window or tab we are done here.
614 VLOG(2) << "ProcessCmdLineImpl: PLACE 4.A";
615 if (command_line
.HasSwitch(extensions::switches::kLoadApps
) &&
616 !IncognitoModePrefs::ShouldLaunchIncognito(
617 command_line
, last_used_profile
->GetPrefs())) {
618 if (!ProcessLoadApps(command_line
, cur_dir
, last_used_profile
))
621 // Return early here to avoid opening a browser window.
622 // The exception is when there are no browser windows, since we don't want
623 // chrome to shut down.
624 // TODO(jackhou): Do this properly once keep-alive is handled by the
625 // background page of apps. Tracked at http://crbug.com/175381
626 if (chrome::GetTotalBrowserCountForProfile(last_used_profile
) != 0)
630 VLOG(2) << "ProcessCmdLineImpl: PLACE 4.B";
631 // Check for --load-and-launch-app.
632 if (command_line
.HasSwitch(apps::kLoadAndLaunchApp
) &&
633 !IncognitoModePrefs::ShouldLaunchIncognito(
634 command_line
, last_used_profile
->GetPrefs())) {
635 base::CommandLine::StringType path
=
636 command_line
.GetSwitchValueNative(apps::kLoadAndLaunchApp
);
638 if (!apps::AppLoadService::Get(last_used_profile
)->LoadAndLaunch(
639 base::FilePath(path
), command_line
, cur_dir
)) {
643 // Return early here since we don't want to open a browser window.
644 // The exception is when there are no browser windows, since we don't want
645 // chrome to shut down.
646 // TODO(jackhou): Do this properly once keep-alive is handled by the
647 // background page of apps. Tracked at http://crbug.com/175381
648 if (chrome::GetTotalBrowserCountForProfile(last_used_profile
) != 0)
653 // Log whether this process was a result of an action in the Windows Jumplist.
654 if (command_line
.HasSwitch(switches::kWinJumplistAction
)) {
655 jumplist::LogJumplistActionFromSwitchValue(
656 command_line
.GetSwitchValueASCII(switches::kWinJumplistAction
));
660 VLOG(2) << "ProcessCmdLineImpl: PLACE 5";
661 chrome::startup::IsProcessStartup is_process_startup
= process_startup
?
662 chrome::startup::IS_PROCESS_STARTUP
:
663 chrome::startup::IS_NOT_PROCESS_STARTUP
;
664 chrome::startup::IsFirstRun is_first_run
= first_run::IsChromeFirstRun() ?
665 chrome::startup::IS_FIRST_RUN
: chrome::startup::IS_NOT_FIRST_RUN
;
666 // |last_opened_profiles| will be empty in the following circumstances:
667 // - This is the first launch. |last_used_profile| is the initial profile.
668 // - The user exited the browser by closing all windows for all
669 // profiles. |last_used_profile| is the profile which owned the last open
671 // - Only incognito windows were open when the browser exited.
672 // |last_used_profile| is the last used incognito profile. Restoring it will
673 // create a browser window for the corresponding original profile.
674 if (last_opened_profiles
.empty()) {
675 VLOG(2) << "ProcessCmdLineImpl: PLACE 6.A";
676 // If the last used profile is locked or was a guest, show the user manager.
677 if (switches::IsNewAvatarMenu()) {
678 ProfileInfoCache
& profile_info
=
679 g_browser_process
->profile_manager()->GetProfileInfoCache();
680 size_t profile_index
= profile_info
.GetIndexOfProfileWithPath(
681 last_used_profile
->GetPath());
682 bool signin_required
= profile_index
!= std::string::npos
&&
683 profile_info
.ProfileIsSigninRequiredAtIndex(profile_index
);
685 // Guest, system or locked profiles cannot be re-opened on startup. The
686 // only exception is if there's already a Guest window open in a separate
687 // process (for example, launching a new browser after clicking on a
688 // downloaded file in Guest mode).
689 bool guest_or_system
= last_used_profile
->IsGuestSession() ||
690 last_used_profile
->IsSystemProfile();
691 bool has_guest_browsers
= guest_or_system
&&
692 chrome::GetTotalBrowserCountForProfile(
693 last_used_profile
->GetOffTheRecordProfile()) > 0;
694 if (signin_required
|| (guest_or_system
&& !has_guest_browsers
)) {
695 profiles::UserManagerProfileSelected action
=
696 command_line
.HasSwitch(switches::kShowAppList
) ?
697 profiles::USER_MANAGER_SELECT_PROFILE_APP_LAUNCHER
:
698 profiles::USER_MANAGER_SELECT_PROFILE_NO_ACTION
;
700 base::FilePath(), profiles::USER_MANAGER_NO_TUTORIAL
, action
);
705 VLOG(2) << "ProcessCmdLineImpl: PLACE 7.A";
706 Profile
* profile_to_open
= last_used_profile
->IsGuestSession() ?
707 last_used_profile
->GetOffTheRecordProfile() : last_used_profile
;
709 VLOG(2) << "ProcessCmdLineImpl: PLACE 8.A";
710 if (!browser_creator
->LaunchBrowser(command_line
, profile_to_open
,
711 cur_dir
, is_process_startup
,
716 VLOG(2) << "ProcessCmdLineImpl: PLACE 6.B";
717 // Guest profiles should not be reopened on startup. This can happen if
718 // the last used profile was a Guest, but other profiles were also open
719 // when Chrome was closed. In this case, pick a different open profile
720 // to be the active one, since the Guest profile is never added to the list
722 if (switches::IsNewAvatarMenu() && last_used_profile
->IsGuestSession()) {
723 DCHECK(!last_opened_profiles
[0]->IsGuestSession());
724 last_used_profile
= last_opened_profiles
[0];
727 // Launch the last used profile with the full command line, and the other
728 // opened profiles without the URLs to launch.
729 base::CommandLine
command_line_without_urls(command_line
.GetProgram());
730 const base::CommandLine::SwitchMap
& switches
= command_line
.GetSwitches();
731 for (base::CommandLine::SwitchMap::const_iterator switch_it
=
733 switch_it
!= switches
.end(); ++switch_it
) {
734 command_line_without_urls
.AppendSwitchNative(switch_it
->first
,
737 VLOG(2) << "ProcessCmdLineImpl: PLACE 7.B";
738 // Launch the profiles in the order they became active.
739 for (Profiles::const_iterator it
= last_opened_profiles
.begin();
740 it
!= last_opened_profiles
.end(); ++it
) {
741 DCHECK(!(*it
)->IsGuestSession());
742 // Don't launch additional profiles which would only open a new tab
743 // page. When restarting after an update, all profiles will reopen last
745 SessionStartupPref startup_pref
=
746 GetSessionStartupPref(command_line
, *it
);
747 if (*it
!= last_used_profile
&&
748 startup_pref
.type
== SessionStartupPref::DEFAULT
&&
749 !HasPendingUncleanExit(*it
))
752 if (!browser_creator
->LaunchBrowser((*it
== last_used_profile
) ?
753 command_line
: command_line_without_urls
, *it
, cur_dir
,
754 is_process_startup
, is_first_run
))
756 // We've launched at least one browser.
757 is_process_startup
= chrome::startup::IS_NOT_PROCESS_STARTUP
;
759 VLOG(2) << "ProcessCmdLineImpl: PLACE 8.B";
760 // This must be done after all profiles have been launched so the observer
761 // knows about all profiles to wait for before activating this one.
762 profile_launch_observer
.Get().set_profile_to_activate(last_used_profile
);
764 VLOG(2) << "ProcessCmdLineImpl: END";
769 bool StartupBrowserCreator::ProcessLoadApps(
770 const base::CommandLine
& command_line
,
771 const base::FilePath
& cur_dir
,
773 base::CommandLine::StringType path_list
=
774 command_line
.GetSwitchValueNative(extensions::switches::kLoadApps
);
776 base::StringTokenizerT
<base::CommandLine::StringType
,
777 base::CommandLine::StringType::const_iterator
>
778 tokenizer(path_list
, FILE_PATH_LITERAL(","));
780 if (!tokenizer
.GetNext())
783 base::FilePath app_absolute_dir
=
784 base::MakeAbsoluteFilePath(base::FilePath(tokenizer
.token()));
785 if (!apps::AppLoadService::Get(profile
)->LoadAndLaunch(
786 app_absolute_dir
, command_line
, cur_dir
)) {
790 while (tokenizer
.GetNext()) {
792 base::MakeAbsoluteFilePath(base::FilePath(tokenizer
.token()));
794 if (!apps::AppLoadService::Get(profile
)->Load(app_absolute_dir
)) {
803 void StartupBrowserCreator::ProcessCommandLineOnProfileCreated(
804 const base::CommandLine
& command_line
,
805 const base::FilePath
& cur_dir
,
807 Profile::CreateStatus status
) {
808 if (status
!= Profile::CREATE_STATUS_INITIALIZED
)
810 ProcessCmdLineImpl(command_line
, cur_dir
, false, profile
, Profiles(), NULL
);
814 void StartupBrowserCreator::ProcessCommandLineAlreadyRunning(
815 const base::CommandLine
& command_line
,
816 const base::FilePath
& cur_dir
,
817 const base::FilePath
& profile_path
) {
818 ProfileManager
* profile_manager
= g_browser_process
->profile_manager();
819 Profile
* profile
= profile_manager
->GetProfileByPath(profile_path
);
821 // The profile isn't loaded yet and so needs to be loaded asynchronously.
823 profile_manager
->CreateProfileAsync(profile_path
,
824 base::Bind(&StartupBrowserCreator::ProcessCommandLineOnProfileCreated
,
825 command_line
, cur_dir
), base::string16(), base::string16(),
830 ProcessCmdLineImpl(command_line
, cur_dir
, false, profile
, Profiles(), NULL
);
834 bool StartupBrowserCreator::ActivatedProfile() {
835 return profile_launch_observer
.Get().activated_profile();
838 bool HasPendingUncleanExit(Profile
* profile
) {
839 return profile
->GetLastSessionExitType() == Profile::EXIT_CRASHED
&&
840 !profile_launch_observer
.Get().HasBeenLaunched(profile
);
843 base::FilePath
GetStartupProfilePath(const base::FilePath
& user_data_dir
,
844 const base::CommandLine
& command_line
) {
845 if (command_line
.HasSwitch(switches::kProfileDirectory
)) {
846 return user_data_dir
.Append(
847 command_line
.GetSwitchValuePath(switches::kProfileDirectory
));
850 // If we are showing the app list then chrome isn't shown so load the app
851 // list's profile rather than chrome's.
852 if (command_line
.HasSwitch(switches::kShowAppList
)) {
853 return AppListService::Get(chrome::HOST_DESKTOP_TYPE_NATIVE
)->
854 GetProfilePath(user_data_dir
);
857 return g_browser_process
->profile_manager()->GetLastUsedProfileDir(