startup_browser_creator_impl.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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_impl.h" 6 7#include <algorithm> 8#include <vector> 9 10#include "apps/app_restore_service.h" 11#include "apps/app_restore_service_factory.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/lazy_instance.h" 18#include "base/memory/scoped_ptr.h" 19#include "base/metrics/histogram.h" 20#include "base/metrics/statistics_recorder.h" 21#include "base/path_service.h" 22#include "base/prefs/pref_service.h" 23#include "base/strings/string_number_conversions.h" 24#include "base/strings/string_split.h" 25#include "base/threading/thread_restrictions.h" 26#include "base/utf_string_conversions.h" 27#include "chrome/browser/auto_launch_trial.h" 28#include "chrome/browser/browser_process.h" 29#include "chrome/browser/custom_handlers/protocol_handler_registry.h" 30#include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h" 31#include "chrome/browser/defaults.h" 32#include "chrome/browser/extensions/extension_creator.h" 33#include "chrome/browser/extensions/extension_service.h" 34#include "chrome/browser/extensions/pack_extension_job.h" 35#include "chrome/browser/first_run/first_run.h" 36#include "chrome/browser/google/google_util.h" 37#include "chrome/browser/infobars/infobar_service.h" 38#include "chrome/browser/net/predictor.h" 39#include "chrome/browser/net/url_fixer_upper.h" 40#include "chrome/browser/notifications/desktop_notification_service.h" 41#include "chrome/browser/performance_monitor/startup_timer.h" 42#include "chrome/browser/prefs/incognito_mode_prefs.h" 43#include "chrome/browser/prefs/session_startup_pref.h" 44#include "chrome/browser/profiles/profile.h" 45#include "chrome/browser/profiles/profile_io_data.h" 46#include "chrome/browser/rlz/rlz.h" 47#include "chrome/browser/sessions/session_restore.h" 48#include "chrome/browser/sessions/session_service.h" 49#include "chrome/browser/sessions/session_service_factory.h" 50#include "chrome/browser/shell_integration.h" 51#include "chrome/browser/ui/app_list/app_list_service.h" 52#include "chrome/browser/ui/browser_commands.h" 53#include "chrome/browser/ui/browser_finder.h" 54#include "chrome/browser/ui/browser_list.h" 55#include "chrome/browser/ui/browser_navigator.h" 56#include "chrome/browser/ui/browser_tabrestore.h" 57#include "chrome/browser/ui/browser_tabstrip.h" 58#include "chrome/browser/ui/browser_window.h" 59#include "chrome/browser/ui/extensions/application_launch.h" 60#include "chrome/browser/ui/host_desktop.h" 61#include "chrome/browser/ui/startup/autolaunch_prompt.h" 62#include "chrome/browser/ui/startup/bad_flags_prompt.h" 63#include "chrome/browser/ui/startup/default_browser_prompt.h" 64#include "chrome/browser/ui/startup/google_api_keys_infobar_delegate.h" 65#include "chrome/browser/ui/startup/obsolete_os_infobar_delegate.h" 66#include "chrome/browser/ui/startup/session_crashed_prompt.h" 67#include "chrome/browser/ui/startup/startup_browser_creator.h" 68#include "chrome/browser/ui/tabs/pinned_tab_codec.h" 69#include "chrome/browser/ui/tabs/tab_strip_model.h" 70#include "chrome/browser/ui/webui/ntp/app_launcher_handler.h" 71#include "chrome/browser/ui/webui/sync_promo/sync_promo_trial.h" 72#include "chrome/browser/ui/webui/sync_promo/sync_promo_ui.h" 73#include "chrome/common/chrome_constants.h" 74#include "chrome/common/chrome_notification_types.h" 75#include "chrome/common/chrome_paths.h" 76#include "chrome/common/chrome_result_codes.h" 77#include "chrome/common/chrome_switches.h" 78#include "chrome/common/chrome_version_info.h" 79#include "chrome/common/extensions/extension_constants.h" 80#include "chrome/common/pref_names.h" 81#include "chrome/common/url_constants.h" 82#include "chrome/installer/util/browser_distribution.h" 83#include "content/public/browser/child_process_security_policy.h" 84#include "content/public/browser/dom_storage_context.h" 85#include "content/public/browser/notification_observer.h" 86#include "content/public/browser/notification_registrar.h" 87#include "content/public/browser/storage_partition.h" 88#include "content/public/browser/web_contents.h" 89#include "content/public/browser/web_contents_view.h" 90#include "grit/locale_settings.h" 91#include "ui/base/l10n/l10n_util.h" 92#include "ui/base/resource/resource_bundle.h" 93#include "ui/gfx/rect.h" 94#include "ui/gfx/screen.h" 95 96#if defined(OS_MACOSX) 97#include "base/mac/mac_util.h" 98#include "chrome/browser/ui/cocoa/keystone_infobar_delegate.h" 99#endif 100 101#if defined(TOOLKIT_GTK) 102#include "chrome/browser/ui/gtk/gtk_util.h" 103#endif 104 105#if defined(OS_WIN) 106#include "apps/app_launch_for_metro_restart_win.h" 107#include "base/win/windows_version.h" 108#endif 109 110#if defined(ENABLE_MANAGED_USERS) 111#include "chrome/browser/managed_mode/managed_user_service.h" 112#include "chrome/browser/managed_mode/managed_user_service_factory.h" 113#endif 114 115using content::ChildProcessSecurityPolicy; 116using content::WebContents; 117using extensions::Extension; 118 119namespace { 120 121// Utility functions ---------------------------------------------------------- 122 123enum LaunchMode { 124 LM_TO_BE_DECIDED = 0, // Possibly direct launch or via a shortcut. 125 LM_AS_WEBAPP, // Launched as a installed web application. 126 LM_WITH_URLS, // Launched with urls in the cmd line. 127 LM_SHORTCUT_NONE, // Not launched from a shortcut. 128 LM_SHORTCUT_NONAME, // Launched from shortcut but no name available. 129 LM_SHORTCUT_UNKNOWN, // Launched from user-defined shortcut. 130 LM_SHORTCUT_QUICKLAUNCH, // Launched from the quick launch bar. 131 LM_SHORTCUT_DESKTOP, // Launched from a desktop shortcut. 132 LM_SHORTCUT_TASKBAR, // Launched from the taskbar. 133 LM_LINUX_MAC_BEOS // Other OS buckets start here. 134}; 135 136#if defined(OS_WIN) 137// Undocumented flag in the startup info structure tells us what shortcut was 138// used to launch the browser. See http://www.catch22.net/tuts/undoc01 for 139// more information. Confirmed to work on XP, Vista and Win7. 140LaunchMode GetLaunchShortcutKind() { 141 STARTUPINFOW si = { sizeof(si) }; 142 GetStartupInfoW(&si); 143 if (si.dwFlags & 0x800) { 144 if (!si.lpTitle) 145 return LM_SHORTCUT_NONAME; 146 string16 shortcut(si.lpTitle); 147 // The windows quick launch path is not localized. 148 if (shortcut.find(L"\\Quick Launch\\") != string16::npos) { 149 if (base::win::GetVersion() >= base::win::VERSION_WIN7) 150 return LM_SHORTCUT_TASKBAR; 151 else 152 return LM_SHORTCUT_QUICKLAUNCH; 153 } 154 scoped_ptr<base::Environment> env(base::Environment::Create()); 155 std::string appdata_path; 156 env->GetVar("USERPROFILE", &appdata_path); 157 if (!appdata_path.empty() && 158 shortcut.find(ASCIIToWide(appdata_path)) != std::wstring::npos) 159 return LM_SHORTCUT_DESKTOP; 160 return LM_SHORTCUT_UNKNOWN; 161 } 162 return LM_SHORTCUT_NONE; 163} 164#else 165// TODO(cpu): Port to other platforms. 166LaunchMode GetLaunchShortcutKind() { 167 return LM_LINUX_MAC_BEOS; 168} 169#endif 170 171// Log in a histogram the frequency of launching by the different methods. See 172// LaunchMode enum for the actual values of the buckets. 173void RecordLaunchModeHistogram(LaunchMode mode) { 174 int bucket = (mode == LM_TO_BE_DECIDED) ? GetLaunchShortcutKind() : mode; 175 UMA_HISTOGRAM_COUNTS_100("Launch.Modes", bucket); 176} 177 178void UrlsToTabs(const std::vector<GURL>& urls, StartupTabs* tabs) { 179 for (size_t i = 0; i < urls.size(); ++i) { 180 StartupTab tab; 181 tab.is_pinned = false; 182 tab.url = urls[i]; 183 tabs->push_back(tab); 184 } 185} 186 187// Return true if the command line option --app-id is used. Set 188// |out_extension| to the app to open, and |out_launch_container| 189// to the type of window into which the app should be open. 190bool GetAppLaunchContainer( 191 Profile* profile, 192 const std::string& app_id, 193 const Extension** out_extension, 194 extension_misc::LaunchContainer* out_launch_container) { 195 196 ExtensionService* extensions_service = profile->GetExtensionService(); 197 const Extension* extension = 198 extensions_service->GetExtensionById(app_id, false); 199 200 // The extension with id |app_id| may have been uninstalled. 201 if (!extension) 202 return false; 203 204 // Don't launch platform apps in incognito mode. 205 if (profile->IsOffTheRecord() && extension->is_platform_app()) 206 return false; 207 208 // Look at preferences to find the right launch container. If no 209 // preference is set, launch as a window. 210 extension_misc::LaunchContainer launch_container = 211 extensions_service->extension_prefs()->GetLaunchContainer( 212 extension, extensions::ExtensionPrefs::LAUNCH_WINDOW); 213 214 *out_extension = extension; 215 *out_launch_container = launch_container; 216 return true; 217} 218 219// Parse two comma-separated integers from string. Return true on success. 220bool ParseCommaSeparatedIntegers(const std::string& str, 221 int* ret_num1, 222 int* ret_num2) { 223 std::vector<std::string> dimensions; 224 base::SplitString(str, ',', &dimensions); 225 if (dimensions.size() != 2) 226 return false; 227 228 int num1, num2; 229 if (!base::StringToInt(dimensions[0], &num1) || 230 !base::StringToInt(dimensions[1], &num2)) 231 return false; 232 233 *ret_num1 = num1; 234 *ret_num2 = num2; 235 return true; 236} 237 238void RecordCmdLineAppHistogram(extensions::Manifest::Type app_type) { 239 AppLauncherHandler::RecordAppLaunchType( 240 extension_misc::APP_LAUNCH_CMD_LINE_APP, 241 app_type); 242} 243 244void RecordAppLaunches(Profile* profile, 245 const std::vector<GURL>& cmd_line_urls, 246 StartupTabs& autolaunch_tabs) { 247 ExtensionService* extension_service = profile->GetExtensionService(); 248 DCHECK(extension_service); 249 for (size_t i = 0; i < cmd_line_urls.size(); ++i) { 250 const extensions::Extension* extension = 251 extension_service->GetInstalledApp(cmd_line_urls.at(i)); 252 if (extension) { 253 AppLauncherHandler::RecordAppLaunchType( 254 extension_misc::APP_LAUNCH_CMD_LINE_URL, 255 extension->GetType()); 256 } 257 } 258 for (size_t i = 0; i < autolaunch_tabs.size(); ++i) { 259 const extensions::Extension* extension = 260 extension_service->GetInstalledApp(autolaunch_tabs.at(i).url); 261 if (extension) { 262 AppLauncherHandler::RecordAppLaunchType( 263 extension_misc::APP_LAUNCH_AUTOLAUNCH, 264 extension->GetType()); 265 } 266 } 267} 268 269bool IsNewTabURL(Profile* profile, const GURL& url) { 270 GURL ntp_url(chrome::kChromeUINewTabURL); 271 return url == ntp_url || 272 (url.is_empty() && profile->GetHomePage() == ntp_url); 273} 274 275void AddSyncPromoTab(Profile* profile, StartupTabs* tabs) { 276 SyncPromoUI::DidShowSyncPromoAtStartup(profile); 277 278 StartupTab sync_promo_tab; 279 GURL continue_url; 280 if (!SyncPromoUI::UseWebBasedSigninFlow()) 281 continue_url = GURL(chrome::kChromeUINewTabURL); 282 sync_promo_tab.url = SyncPromoUI::GetSyncPromoURL( 283 continue_url, SyncPromoUI::SOURCE_START_PAGE, false); 284 sync_promo_tab.is_pinned = false; 285 286 // No need to add if the sync promo is already in the startup list. 287 for (StartupTabs::const_iterator it = tabs->begin(); it != tabs->end(); 288 ++it) { 289 if (it->url == sync_promo_tab.url) 290 return; 291 } 292 293 tabs->insert(tabs->begin(), sync_promo_tab); 294 295 // If the next URL is the NTP then remove it, effectively replacing the NTP 296 // with the sync promo. This behavior is desired because completing or 297 // skipping the sync promo causes a redirect to the NTP. 298 if (tabs->size() > 1 && IsNewTabURL(profile, tabs->at(1).url)) 299 tabs->erase(tabs->begin() + 1); 300} 301 302class WebContentsCloseObserver : public content::NotificationObserver { 303 public: 304 WebContentsCloseObserver() : contents_(NULL) {} 305 virtual ~WebContentsCloseObserver() {} 306 307 void SetContents(content::WebContents* contents) { 308 DCHECK(!contents_); 309 contents_ = contents; 310 311 registrar_.Add(this, 312 content::NOTIFICATION_WEB_CONTENTS_DESTROYED, 313 content::Source<content::WebContents>(contents_)); 314 } 315 316 content::WebContents* contents() { return contents_; } 317 318 private: 319 // content::NotificationObserver overrides: 320 virtual void Observe(int type, 321 const content::NotificationSource& source, 322 const content::NotificationDetails& details) OVERRIDE { 323 DCHECK_EQ(type, content::NOTIFICATION_WEB_CONTENTS_DESTROYED); 324 contents_ = NULL; 325 } 326 327 content::WebContents* contents_; 328 content::NotificationRegistrar registrar_; 329 330 DISALLOW_COPY_AND_ASSIGN(WebContentsCloseObserver); 331}; 332 333} // namespace 334 335namespace internals { 336 337GURL GetWelcomePageURL() { 338 std::string welcome_url = l10n_util::GetStringUTF8(IDS_WELCOME_PAGE_URL); 339 return GURL(welcome_url); 340} 341 342} // namespace internals 343 344StartupBrowserCreatorImpl::StartupBrowserCreatorImpl( 345 const base::FilePath& cur_dir, 346 const CommandLine& command_line, 347 chrome::startup::IsFirstRun is_first_run) 348 : cur_dir_(cur_dir), 349 command_line_(command_line), 350 profile_(NULL), 351 browser_creator_(NULL), 352 is_first_run_(is_first_run == chrome::startup::IS_FIRST_RUN) { 353} 354 355StartupBrowserCreatorImpl::StartupBrowserCreatorImpl( 356 const base::FilePath& cur_dir, 357 const CommandLine& command_line, 358 StartupBrowserCreator* browser_creator, 359 chrome::startup::IsFirstRun is_first_run) 360 : cur_dir_(cur_dir), 361 command_line_(command_line), 362 profile_(NULL), 363 browser_creator_(browser_creator), 364 is_first_run_(is_first_run == chrome::startup::IS_FIRST_RUN) { 365} 366 367StartupBrowserCreatorImpl::~StartupBrowserCreatorImpl() { 368} 369 370bool StartupBrowserCreatorImpl::Launch(Profile* profile, 371 const std::vector<GURL>& urls_to_open, 372 bool process_startup, 373 chrome::HostDesktopType desktop_type) { 374 DCHECK(profile); 375 profile_ = profile; 376 377 if (command_line_.HasSwitch(switches::kDnsLogDetails)) 378 chrome_browser_net::EnablePredictorDetailedLog(true); 379 if (command_line_.HasSwitch(switches::kDnsPrefetchDisable) && 380 profile->GetNetworkPredictor()) { 381 profile->GetNetworkPredictor()->EnablePredictor(false); 382 } 383 384 AppListService::InitAll(profile); 385 if (command_line_.HasSwitch(switches::kShowAppList)) { 386 AppListService::RecordShowTimings(command_line_); 387 AppListService::Get()->ShowAppList(profile); 388 return true; 389 } 390 391 // Open the required browser windows and tabs. First, see if 392 // we're being run as an application window. If so, the user 393 // opened an app shortcut. Don't restore tabs or open initial 394 // URLs in that case. The user should see the window as an app, 395 // not as chrome. 396 // Special case is when app switches are passed but we do want to restore 397 // session. In that case open app window + focus it after session is restored. 398 content::WebContents* app_contents = NULL; 399 if (OpenApplicationWindow(profile, &app_contents)) { 400 RecordLaunchModeHistogram(LM_AS_WEBAPP); 401 } else { 402 RecordLaunchModeHistogram(urls_to_open.empty() ? 403 LM_TO_BE_DECIDED : LM_WITH_URLS); 404 405 ProcessLaunchURLs(process_startup, urls_to_open, desktop_type); 406 407 // If this is an app launch, but we didn't open an app window, it may 408 // be an app tab. 409 OpenApplicationTab(profile); 410 411#if defined(OS_MACOSX) 412 if (process_startup) { 413 // Check whether the auto-update system needs to be promoted from user 414 // to system. 415 KeystoneInfoBar::PromotionInfoBar(profile); 416 } 417#endif 418 } 419 420#if defined(OS_WIN) 421 if (process_startup) 422 ShellIntegration::MigrateChromiumShortcuts(); 423#endif // defined(OS_WIN) 424 425 return true; 426} 427 428void StartupBrowserCreatorImpl::ExtractOptionalAppWindowSize( 429 gfx::Rect* bounds) { 430 if (command_line_.HasSwitch(switches::kAppWindowSize)) { 431 int width, height; 432 width = height = 0; 433 std::string switch_value = 434 command_line_.GetSwitchValueASCII(switches::kAppWindowSize); 435 if (ParseCommaSeparatedIntegers(switch_value, &width, &height)) { 436 // TODO(scottmg): NativeScreen might be wrong. http://crbug.com/133312 437 const gfx::Rect work_area = 438 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().work_area(); 439 width = std::min(width, work_area.width()); 440 height = std::min(height, work_area.height()); 441 bounds->set_size(gfx::Size(width, height)); 442 bounds->set_x((work_area.width() - bounds->width()) / 2); 443 // TODO(nkostylev): work_area does include launcher but should not. 444 // Launcher auto hide pref is synced and is most likely not applied here. 445 bounds->set_y((work_area.height() - bounds->height()) / 2); 446 } 447 } 448} 449 450bool StartupBrowserCreatorImpl::IsAppLaunch(std::string* app_url, 451 std::string* app_id) { 452 if (command_line_.HasSwitch(switches::kApp)) { 453 if (app_url) 454 *app_url = command_line_.GetSwitchValueASCII(switches::kApp); 455 return true; 456 } 457 if (command_line_.HasSwitch(switches::kAppId)) { 458 if (app_id) 459 *app_id = command_line_.GetSwitchValueASCII(switches::kAppId); 460 return true; 461 } 462 return false; 463} 464 465bool StartupBrowserCreatorImpl::OpenApplicationTab(Profile* profile) { 466 std::string app_id; 467 // App shortcuts to URLs always open in an app window. Because this 468 // function will open an app that should be in a tab, there is no need 469 // to look at the app URL. OpenApplicationWindow() will open app url 470 // shortcuts. 471 if (!IsAppLaunch(NULL, &app_id) || app_id.empty()) 472 return false; 473 474 extension_misc::LaunchContainer launch_container; 475 const Extension* extension; 476 if (!GetAppLaunchContainer(profile, app_id, &extension, &launch_container)) 477 return false; 478 479 // If the user doesn't want to open a tab, fail. 480 if (launch_container != extension_misc::LAUNCH_TAB) 481 return false; 482 483 RecordCmdLineAppHistogram(extension->GetType()); 484 485 WebContents* app_tab = chrome::OpenApplication(chrome::AppLaunchParams( 486 profile, extension, extension_misc::LAUNCH_TAB, NEW_FOREGROUND_TAB)); 487 return (app_tab != NULL); 488} 489 490bool StartupBrowserCreatorImpl::OpenApplicationWindow( 491 Profile* profile, 492 content::WebContents** out_app_contents) { 493 // Set |out_app_contents| to NULL early on (just in case). 494 if (out_app_contents) 495 *out_app_contents = NULL; 496 497 std::string url_string, app_id; 498 if (!IsAppLaunch(&url_string, &app_id)) 499 return false; 500 501 // This can fail if the app_id is invalid. It can also fail if the 502 // extension is external, and has not yet been installed. 503 // TODO(skerner): Do something reasonable here. Pop up a warning panel? 504 // Open an URL to the gallery page of the extension id? 505 if (!app_id.empty()) { 506 extension_misc::LaunchContainer launch_container; 507 const Extension* extension; 508 if (!GetAppLaunchContainer(profile, app_id, &extension, &launch_container)) 509 return false; 510 511 // TODO(skerner): Could pass in |extension| and |launch_container|, 512 // and avoid calling GetAppLaunchContainer() both here and in 513 // OpenApplicationTab(). 514 515 if (launch_container == extension_misc::LAUNCH_TAB) 516 return false; 517 518 RecordCmdLineAppHistogram(extension->GetType()); 519 520 chrome::AppLaunchParams params(profile, extension, 521 launch_container, NEW_WINDOW); 522 params.command_line = &command_line_; 523 params.current_directory = cur_dir_; 524 WebContents* tab_in_app_window = chrome::OpenApplication(params); 525 526 if (out_app_contents) 527 *out_app_contents = tab_in_app_window; 528 529 // Platform apps fire off a launch event which may or may not open a window. 530 return (tab_in_app_window != NULL || extension->is_platform_app()); 531 } 532 533 if (url_string.empty()) 534 return false; 535 536#if defined(OS_WIN) // Fix up Windows shortcuts. 537 ReplaceSubstringsAfterOffset(&url_string, 0, "\\x", "%"); 538#endif 539 GURL url(url_string); 540 541 // Restrict allowed URLs for --app switch. 542 if (!url.is_empty() && url.is_valid()) { 543 ChildProcessSecurityPolicy* policy = 544 ChildProcessSecurityPolicy::GetInstance(); 545 if (policy->IsWebSafeScheme(url.scheme()) || 546 url.SchemeIs(chrome::kFileScheme)) { 547 const extensions::Extension* extension = 548 profile->GetExtensionService()->GetInstalledApp(url); 549 if (extension) { 550 RecordCmdLineAppHistogram(extension->GetType()); 551 } else { 552 AppLauncherHandler::RecordAppLaunchType( 553 extension_misc::APP_LAUNCH_CMD_LINE_APP_LEGACY, 554 extensions::Manifest::TYPE_HOSTED_APP); 555 } 556 557 gfx::Rect override_bounds; 558 ExtractOptionalAppWindowSize(&override_bounds); 559 560 WebContents* app_tab = chrome::OpenAppShortcutWindow(profile, 561 url, 562 override_bounds); 563 564 if (out_app_contents) 565 *out_app_contents = app_tab; 566 567 return (app_tab != NULL); 568 } 569 } 570 return false; 571} 572 573void StartupBrowserCreatorImpl::ProcessLaunchURLs( 574 bool process_startup, 575 const std::vector<GURL>& urls_to_open, 576 chrome::HostDesktopType desktop_type) { 577 // If we're starting up in "background mode" (no open browser window) then 578 // don't open any browser windows, unless kAutoLaunchAtStartup is also 579 // specified. 580 if (process_startup && 581 command_line_.HasSwitch(switches::kNoStartupWindow) && 582 !command_line_.HasSwitch(switches::kAutoLaunchAtStartup)) { 583 return; 584 } 585 586// TODO(tapted): Move this to startup_browser_creator_win.cc after refactor. 587#if defined(OS_WIN) 588 if (base::win::GetVersion() >= base::win::VERSION_WIN8) { 589 // See if there are apps for this profile that should be launched on startup 590 // due to a switch from Metro mode. 591 apps::HandleAppLaunchForMetroRestart(profile_); 592 } 593#endif 594 595 if (process_startup && ProcessStartupURLs(urls_to_open, desktop_type)) { 596 // ProcessStartupURLs processed the urls, nothing else to do. 597 return; 598 } 599 600 chrome::startup::IsProcessStartup is_process_startup = process_startup ? 601 chrome::startup::IS_PROCESS_STARTUP : 602 chrome::startup::IS_NOT_PROCESS_STARTUP; 603 if (!process_startup) { 604 // Even if we're not starting a new process, this may conceptually be 605 // "startup" for the user and so should be handled in a similar way. Eg., 606 // Chrome may have been running in the background due to an app with a 607 // background page being installed, or running with only an app window 608 // displayed. 609 SessionService* service = SessionServiceFactory::GetForProfile(profile_); 610 if (service && service->ShouldNewWindowStartSession()) { 611 // Restore the last session if any. 612 if (!HasPendingUncleanExit(profile_) && 613 service->RestoreIfNecessary(urls_to_open)) { 614 return; 615 } 616 // Open user-specified URLs like pinned tabs and startup tabs. 617 Browser* browser = ProcessSpecifiedURLs(urls_to_open, desktop_type); 618 if (browser) { 619 AddInfoBarsIfNecessary(browser, is_process_startup); 620 return; 621 } 622 } 623 } 624 625 // Session startup didn't occur, open the urls. 626 Browser* browser = NULL; 627 std::vector<GURL> adjust_urls = urls_to_open; 628 if (adjust_urls.empty()) { 629 AddStartupURLs(&adjust_urls); 630 if (StartupBrowserCreatorImpl::OpenStartupURLsInExistingBrowser( 631 profile_, adjust_urls)) 632 return; 633 } else if (!command_line_.HasSwitch(switches::kOpenInNewWindow)) { 634 // Always open a list of urls in a window on the native desktop. 635 browser = chrome::FindTabbedBrowser(profile_, false, 636 chrome::HOST_DESKTOP_TYPE_NATIVE); 637 } 638 // This will launch a browser; prevent session restore. 639 StartupBrowserCreator::in_synchronous_profile_launch_ = true; 640 browser = OpenURLsInBrowser(browser, process_startup, adjust_urls, 641 desktop_type); 642 StartupBrowserCreator::in_synchronous_profile_launch_ = false; 643 AddInfoBarsIfNecessary(browser, is_process_startup); 644} 645 646bool StartupBrowserCreatorImpl::ProcessStartupURLs( 647 const std::vector<GURL>& urls_to_open, 648 chrome::HostDesktopType desktop_type) { 649 VLOG(1) << "StartupBrowserCreatorImpl::ProcessStartupURLs"; 650 SessionStartupPref pref = 651 StartupBrowserCreator::GetSessionStartupPref(command_line_, profile_); 652 if (pref.type == SessionStartupPref::LAST) 653 VLOG(1) << "Pref: last"; 654 else if (pref.type == SessionStartupPref::URLS) 655 VLOG(1) << "Pref: urls"; 656 else if (pref.type == SessionStartupPref::DEFAULT) 657 VLOG(1) << "Pref: default"; 658 659 apps::AppRestoreService* restore_service = 660 apps::AppRestoreServiceFactory::GetForProfile(profile_); 661 // NULL in incognito mode. 662 if (restore_service) { 663 restore_service->HandleStartup(apps::AppRestoreService::ShouldRestoreApps( 664 StartupBrowserCreator::WasRestarted())); 665 } 666 667 if (pref.type == SessionStartupPref::LAST) { 668 if (profile_->GetLastSessionExitType() == Profile::EXIT_CRASHED && 669 !command_line_.HasSwitch(switches::kRestoreLastSession)) { 670 // The last session crashed. It's possible automatically loading the 671 // page will trigger another crash, locking the user out of chrome. 672 // To avoid this, don't restore on startup but instead show the crashed 673 // infobar. 674 VLOG(1) << "Unclean exit; not processing"; 675 return false; 676 } 677 678 uint32 restore_behavior = SessionRestore::SYNCHRONOUS; 679 if (browser_defaults::kAlwaysCreateTabbedBrowserOnSessionRestore || 680 CommandLine::ForCurrentProcess()->HasSwitch( 681 switches::kCreateBrowserOnStartupForTests)) { 682 restore_behavior |= SessionRestore::ALWAYS_CREATE_TABBED_BROWSER; 683 } 684 685#if defined(OS_MACOSX) 686 // On Mac, when restoring a session with no windows, suppress the creation 687 // of a new window in the case where the system is launching Chrome via a 688 // login item or Lion's resume feature. 689 if (base::mac::WasLaunchedAsLoginOrResumeItem()) { 690 restore_behavior = restore_behavior & 691 ~SessionRestore::ALWAYS_CREATE_TABBED_BROWSER; 692 } 693#endif 694 695 // Pause the StartupTimer. Since the restore here is synchronous, we can 696 // keep these two metrics (browser startup time and session restore time) 697 // separate. 698 performance_monitor::StartupTimer::PauseTimer(); 699 700 // The startup code only executes for browsers launched in desktop mode. 701 // i.e. HOST_DESKTOP_TYPE_NATIVE. Ash should never get here. 702 Browser* browser = SessionRestore::RestoreSession( 703 profile_, NULL, desktop_type, restore_behavior, 704 urls_to_open); 705 706 performance_monitor::StartupTimer::UnpauseTimer(); 707 708 AddInfoBarsIfNecessary(browser, chrome::startup::IS_PROCESS_STARTUP); 709 return true; 710 } 711 712 Browser* browser = ProcessSpecifiedURLs(urls_to_open, desktop_type); 713 if (!browser) 714 return false; 715 716 AddInfoBarsIfNecessary(browser, chrome::startup::IS_PROCESS_STARTUP); 717 718 // Session restore may occur if the startup preference is "last" or if the 719 // crash infobar is displayed. Otherwise, it's safe for the DOM storage system 720 // to start deleting leftover data. 721 if (pref.type != SessionStartupPref::LAST && 722 !HasPendingUncleanExit(profile_)) { 723 content::BrowserContext::GetDefaultStoragePartition(profile_)-> 724 GetDOMStorageContext()->StartScavengingUnusedSessionStorage(); 725 } 726 727 return true; 728} 729 730Browser* StartupBrowserCreatorImpl::ProcessSpecifiedURLs( 731 const std::vector<GURL>& urls_to_open, 732 chrome::HostDesktopType desktop_type) { 733 SessionStartupPref pref = 734 StartupBrowserCreator::GetSessionStartupPref(command_line_, profile_); 735 StartupTabs tabs; 736 // Pinned tabs should not be displayed when chrome is launched in incognito 737 // mode. Also, no pages should be opened automatically if the session 738 // crashed. Otherwise it might trigger another crash, locking the user out of 739 // chrome. The crash infobar is shown in this case. 740 if (!IncognitoModePrefs::ShouldLaunchIncognito(command_line_, 741 profile_->GetPrefs()) && 742 !HasPendingUncleanExit(profile_)) { 743 tabs = PinnedTabCodec::ReadPinnedTabs(profile_); 744 } 745 746 RecordAppLaunches(profile_, urls_to_open, tabs); 747 748 if (!urls_to_open.empty()) { 749 // If urls were specified on the command line, use them. 750 UrlsToTabs(urls_to_open, &tabs); 751 } else if (pref.type == SessionStartupPref::DEFAULT || 752 (is_first_run_ && 753 browser_creator_ && !browser_creator_->first_run_tabs_.empty())) { 754 std::vector<GURL> urls; 755 AddStartupURLs(&urls); 756 UrlsToTabs(urls, &tabs); 757 } else if (pref.type == SessionStartupPref::URLS && !pref.urls.empty() && 758 !HasPendingUncleanExit(profile_)) { 759 // Only use the set of urls specified in preferences if nothing was 760 // specified on the command line. Filter out any urls that are to be 761 // restored by virtue of having been previously pinned. 762 AddUniqueURLs(pref.urls, &tabs); 763 } else if (pref.type == SessionStartupPref::HOMEPAGE) { 764 // If 'homepage' selected, either by the user or by a policy, we should 765 // have migrated them to another value. 766 NOTREACHED() << "SessionStartupPref has deprecated type HOMEPAGE"; 767 } 768 769 if (pref.type != SessionStartupPref::LAST && 770 SyncPromoUI::ShouldShowSyncPromoAtStartup(profile_, is_first_run_)) { 771 AddSyncPromoTab(profile_, &tabs); 772 } 773 774 if (tabs.empty()) 775 return NULL; 776 777 Browser* browser = OpenTabsInBrowser(NULL, true, tabs, desktop_type); 778 return browser; 779} 780 781void StartupBrowserCreatorImpl::AddUniqueURLs(const std::vector<GURL>& urls, 782 StartupTabs* tabs) { 783 size_t num_existing_tabs = tabs->size(); 784 for (size_t i = 0; i < urls.size(); ++i) { 785 bool in_tabs = false; 786 for (size_t j = 0; j < num_existing_tabs; ++j) { 787 if (urls[i] == (*tabs)[j].url) { 788 in_tabs = true; 789 break; 790 } 791 } 792 if (!in_tabs) { 793 StartupTab tab; 794 tab.is_pinned = false; 795 tab.url = urls[i]; 796 tabs->push_back(tab); 797 } 798 } 799} 800 801Browser* StartupBrowserCreatorImpl::OpenURLsInBrowser( 802 Browser* browser, 803 bool process_startup, 804 const std::vector<GURL>& urls, 805 chrome::HostDesktopType desktop_type) { 806 StartupTabs tabs; 807 UrlsToTabs(urls, &tabs); 808 return OpenTabsInBrowser(browser, process_startup, tabs, desktop_type); 809} 810 811Browser* StartupBrowserCreatorImpl::OpenTabsInBrowser( 812 Browser* browser, 813 bool process_startup, 814 const StartupTabs& tabs, 815 chrome::HostDesktopType desktop_type) { 816 DCHECK(!tabs.empty()); 817 818 // If we don't yet have a profile, try to use the one we're given from 819 // |browser|. While we may not end up actually using |browser| (since it 820 // could be a popup window), we can at least use the profile. 821 if (!profile_ && browser) 822 profile_ = browser->profile(); 823 824 if (!browser || !browser->is_type_tabbed()) { 825 browser = new Browser(Browser::CreateParams(profile_, desktop_type)); 826 } else { 827#if defined(TOOLKIT_GTK) 828 // Setting the time of the last action on the window here allows us to steal 829 // focus, which is what the user wants when opening a new tab in an existing 830 // browser window. 831 gtk_util::SetWMLastUserActionTime(browser->window()->GetNativeWindow()); 832#endif 833 } 834 835 // In kiosk mode, we want to always be fullscreen, so switch to that now. 836 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode)) 837 chrome::ToggleFullscreenMode(browser); 838 839 bool first_tab = true; 840 ProtocolHandlerRegistry* registry = profile_ ? 841 ProtocolHandlerRegistryFactory::GetForProfile(profile_) : NULL; 842 for (size_t i = 0; i < tabs.size(); ++i) { 843 // We skip URLs that we'd have to launch an external protocol handler for. 844 // This avoids us getting into an infinite loop asking ourselves to open 845 // a URL, should the handler be (incorrectly) configured to be us. Anyone 846 // asking us to open such a URL should really ask the handler directly. 847 bool handled_by_chrome = ProfileIOData::IsHandledURL(tabs[i].url) || 848 (registry && registry->IsHandledProtocol(tabs[i].url.scheme())); 849 if (!process_startup && !handled_by_chrome) 850 continue; 851 852 int add_types = first_tab ? TabStripModel::ADD_ACTIVE : 853 TabStripModel::ADD_NONE; 854 add_types |= TabStripModel::ADD_FORCE_INDEX; 855 if (tabs[i].is_pinned) 856 add_types |= TabStripModel::ADD_PINNED; 857 858 chrome::NavigateParams params(browser, tabs[i].url, 859 content::PAGE_TRANSITION_AUTO_TOPLEVEL); 860 params.disposition = first_tab ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB; 861 params.tabstrip_add_types = add_types; 862 params.extension_app_id = tabs[i].app_id; 863 864#if defined(ENABLE_RLZ) 865 if (process_startup && 866 google_util::IsGoogleHomePageUrl(tabs[i].url.spec())) { 867 params.extra_headers = RLZTracker::GetAccessPointHttpHeader( 868 RLZTracker::CHROME_HOME_PAGE); 869 } 870#endif 871 872 chrome::Navigate(¶ms); 873 874 first_tab = false; 875 } 876 if (!browser->tab_strip_model()->GetActiveWebContents()) { 877 // TODO: this is a work around for 110909. Figure out why it's needed. 878 if (!browser->tab_strip_model()->count()) 879 chrome::AddBlankTabAt(browser, -1, true); 880 else 881 browser->tab_strip_model()->ActivateTabAt(0, false); 882 } 883 884 // The default behavior is to show the window, as expressed by the default 885 // value of StartupBrowserCreated::show_main_browser_window_. If this was set 886 // to true ahead of this place, it means another task must have been spawned 887 // to take care of that. 888 if (!browser_creator_ || browser_creator_->show_main_browser_window()) 889 browser->window()->Show(); 890 891 return browser; 892} 893 894void StartupBrowserCreatorImpl::AddInfoBarsIfNecessary( 895 Browser* browser, 896 chrome::startup::IsProcessStartup is_process_startup) { 897 if (!browser || !profile_ || browser->tab_strip_model()->count() == 0) 898 return; 899 900 if (HasPendingUncleanExit(browser->profile())) 901 SessionCrashedInfoBarDelegate::Create(browser); 902 903 // The below info bars are only added to the first profile which is launched. 904 // Other profiles might be restoring the browsing sessions asynchronously, 905 // so we cannot add the info bars to the focused tabs here. 906 if (is_process_startup == chrome::startup::IS_PROCESS_STARTUP) { 907 chrome::ShowBadFlagsPrompt(browser); 908 if (!command_line_.HasSwitch(switches::kTestType)) { 909 GoogleApiKeysInfoBarDelegate::Create( 910 InfoBarService::FromWebContents( 911 browser->tab_strip_model()->GetActiveWebContents())); 912 913 // TODO(phajdan.jr): Always enable after migrating bots: 914 // http://crbug.com/170262 . 915 chrome::ObsoleteOSInfoBarDelegate::Create( 916 InfoBarService::FromWebContents( 917 browser->tab_strip_model()->GetActiveWebContents())); 918 } 919 920 if (browser_defaults::kOSSupportsOtherBrowsers && 921 !command_line_.HasSwitch(switches::kNoDefaultBrowserCheck)) { 922 // Generally, the default browser prompt should not be shown on first 923 // run. However, when the set-as-default dialog has been suppressed, we 924 // need to allow it. 925 if ((!is_first_run_ || 926 (browser_creator_ && 927 browser_creator_->is_default_browser_dialog_suppressed())) && 928 !chrome::ShowAutolaunchPrompt(browser)) { 929 chrome::ShowDefaultBrowserPrompt(profile_, 930 browser->host_desktop_type()); 931 } 932 } 933 } 934} 935 936 937void StartupBrowserCreatorImpl::AddStartupURLs( 938 std::vector<GURL>* startup_urls) const { 939 // If we have urls specified by the first run master preferences use them 940 // and nothing else. 941 if (browser_creator_ && startup_urls->empty()) { 942 if (!browser_creator_->first_run_tabs_.empty()) { 943 std::vector<GURL>::iterator it = 944 browser_creator_->first_run_tabs_.begin(); 945 while (it != browser_creator_->first_run_tabs_.end()) { 946 // Replace magic names for the actual urls. 947 if (it->host() == "new_tab_page") { 948 startup_urls->push_back(GURL(chrome::kChromeUINewTabURL)); 949 } else if (it->host() == "welcome_page") { 950 startup_urls->push_back(internals::GetWelcomePageURL()); 951 } else { 952 startup_urls->push_back(*it); 953 } 954 ++it; 955 } 956 browser_creator_->first_run_tabs_.clear(); 957 } 958 } 959 960 // Otherwise open at least the new tab page (and the welcome page, if this 961 // is the first time the browser is being started), or the set of URLs 962 // specified on the command line. 963 if (startup_urls->empty()) { 964 startup_urls->push_back(GURL(chrome::kChromeUINewTabURL)); 965 if (first_run::ShouldShowWelcomePage()) 966 startup_urls->push_back(internals::GetWelcomePageURL()); 967 } 968} 969 970#if !defined(OS_WIN) 971// static 972bool StartupBrowserCreatorImpl::OpenStartupURLsInExistingBrowser( 973 Profile* profile, 974 const std::vector<GURL>& startup_urls) { 975 return false; 976} 977#endif 978