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