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