startup_browser_creator_impl.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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/strings/utf_string_conversions.h" 26#include "base/threading/thread_restrictions.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 sync_promo_tab.url = SyncPromoUI::GetSyncPromoURL( 280 SyncPromoUI::SOURCE_START_PAGE, false); 281 sync_promo_tab.is_pinned = false; 282 283 // No need to add if the sync promo is already in the startup list. 284 for (StartupTabs::const_iterator it = tabs->begin(); it != tabs->end(); 285 ++it) { 286 if (it->url == sync_promo_tab.url) 287 return; 288 } 289 290 tabs->insert(tabs->begin(), sync_promo_tab); 291 292 // If the next URL is the NTP then remove it, effectively replacing the NTP 293 // with the sync promo. This behavior is desired because completing or 294 // skipping the sync promo causes a redirect to the NTP. 295 if (tabs->size() > 1 && IsNewTabURL(profile, tabs->at(1).url)) 296 tabs->erase(tabs->begin() + 1); 297} 298 299class WebContentsCloseObserver : public content::NotificationObserver { 300 public: 301 WebContentsCloseObserver() : contents_(NULL) {} 302 virtual ~WebContentsCloseObserver() {} 303 304 void SetContents(content::WebContents* contents) { 305 DCHECK(!contents_); 306 contents_ = contents; 307 308 registrar_.Add(this, 309 content::NOTIFICATION_WEB_CONTENTS_DESTROYED, 310 content::Source<content::WebContents>(contents_)); 311 } 312 313 content::WebContents* contents() { return contents_; } 314 315 private: 316 // content::NotificationObserver overrides: 317 virtual void Observe(int type, 318 const content::NotificationSource& source, 319 const content::NotificationDetails& details) OVERRIDE { 320 DCHECK_EQ(type, content::NOTIFICATION_WEB_CONTENTS_DESTROYED); 321 contents_ = NULL; 322 } 323 324 content::WebContents* contents_; 325 content::NotificationRegistrar registrar_; 326 327 DISALLOW_COPY_AND_ASSIGN(WebContentsCloseObserver); 328}; 329 330} // namespace 331 332namespace internals { 333 334GURL GetWelcomePageURL() { 335 std::string welcome_url = l10n_util::GetStringUTF8(IDS_WELCOME_PAGE_URL); 336 return GURL(welcome_url); 337} 338 339} // namespace internals 340 341StartupBrowserCreatorImpl::StartupBrowserCreatorImpl( 342 const base::FilePath& cur_dir, 343 const CommandLine& command_line, 344 chrome::startup::IsFirstRun is_first_run) 345 : cur_dir_(cur_dir), 346 command_line_(command_line), 347 profile_(NULL), 348 browser_creator_(NULL), 349 is_first_run_(is_first_run == chrome::startup::IS_FIRST_RUN) { 350} 351 352StartupBrowserCreatorImpl::StartupBrowserCreatorImpl( 353 const base::FilePath& cur_dir, 354 const CommandLine& command_line, 355 StartupBrowserCreator* browser_creator, 356 chrome::startup::IsFirstRun is_first_run) 357 : cur_dir_(cur_dir), 358 command_line_(command_line), 359 profile_(NULL), 360 browser_creator_(browser_creator), 361 is_first_run_(is_first_run == chrome::startup::IS_FIRST_RUN) { 362} 363 364StartupBrowserCreatorImpl::~StartupBrowserCreatorImpl() { 365} 366 367bool StartupBrowserCreatorImpl::Launch(Profile* profile, 368 const std::vector<GURL>& urls_to_open, 369 bool process_startup, 370 chrome::HostDesktopType desktop_type) { 371 DCHECK(profile); 372 profile_ = profile; 373 374 if (command_line_.HasSwitch(switches::kDnsLogDetails)) 375 chrome_browser_net::EnablePredictorDetailedLog(true); 376 if (command_line_.HasSwitch(switches::kDnsPrefetchDisable) && 377 profile->GetNetworkPredictor()) { 378 profile->GetNetworkPredictor()->EnablePredictor(false); 379 } 380 381 AppListService::InitAll(profile); 382 if (command_line_.HasSwitch(switches::kShowAppList)) { 383 AppListService::RecordShowTimings(command_line_); 384 AppListService::Get()->ShowAppList(profile); 385 return true; 386 } 387 388 // Open the required browser windows and tabs. First, see if 389 // we're being run as an application window. If so, the user 390 // opened an app shortcut. Don't restore tabs or open initial 391 // URLs in that case. The user should see the window as an app, 392 // not as chrome. 393 // Special case is when app switches are passed but we do want to restore 394 // session. In that case open app window + focus it after session is restored. 395 content::WebContents* app_contents = NULL; 396 if (OpenApplicationWindow(profile, &app_contents)) { 397 RecordLaunchModeHistogram(LM_AS_WEBAPP); 398 } else { 399 RecordLaunchModeHistogram(urls_to_open.empty() ? 400 LM_TO_BE_DECIDED : LM_WITH_URLS); 401 402 ProcessLaunchURLs(process_startup, urls_to_open, desktop_type); 403 404 // If this is an app launch, but we didn't open an app window, it may 405 // be an app tab. 406 OpenApplicationTab(profile); 407 408#if defined(OS_MACOSX) 409 if (process_startup) { 410 // Check whether the auto-update system needs to be promoted from user 411 // to system. 412 KeystoneInfoBar::PromotionInfoBar(profile); 413 } 414#endif 415 } 416 417#if defined(OS_WIN) 418 if (process_startup) 419 ShellIntegration::MigrateChromiumShortcuts(); 420#endif // defined(OS_WIN) 421 422 return true; 423} 424 425void StartupBrowserCreatorImpl::ExtractOptionalAppWindowSize( 426 gfx::Rect* bounds) { 427 if (command_line_.HasSwitch(switches::kAppWindowSize)) { 428 int width, height; 429 width = height = 0; 430 std::string switch_value = 431 command_line_.GetSwitchValueASCII(switches::kAppWindowSize); 432 if (ParseCommaSeparatedIntegers(switch_value, &width, &height)) { 433 // TODO(scottmg): NativeScreen might be wrong. http://crbug.com/133312 434 const gfx::Rect work_area = 435 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().work_area(); 436 width = std::min(width, work_area.width()); 437 height = std::min(height, work_area.height()); 438 bounds->set_size(gfx::Size(width, height)); 439 bounds->set_x((work_area.width() - bounds->width()) / 2); 440 // TODO(nkostylev): work_area does include launcher but should not. 441 // Launcher auto hide pref is synced and is most likely not applied here. 442 bounds->set_y((work_area.height() - bounds->height()) / 2); 443 } 444 } 445} 446 447bool StartupBrowserCreatorImpl::IsAppLaunch(std::string* app_url, 448 std::string* app_id) { 449 if (command_line_.HasSwitch(switches::kApp)) { 450 if (app_url) 451 *app_url = command_line_.GetSwitchValueASCII(switches::kApp); 452 return true; 453 } 454 if (command_line_.HasSwitch(switches::kAppId)) { 455 if (app_id) 456 *app_id = command_line_.GetSwitchValueASCII(switches::kAppId); 457 return true; 458 } 459 return false; 460} 461 462bool StartupBrowserCreatorImpl::OpenApplicationTab(Profile* profile) { 463 std::string app_id; 464 // App shortcuts to URLs always open in an app window. Because this 465 // function will open an app that should be in a tab, there is no need 466 // to look at the app URL. OpenApplicationWindow() will open app url 467 // shortcuts. 468 if (!IsAppLaunch(NULL, &app_id) || app_id.empty()) 469 return false; 470 471 extension_misc::LaunchContainer launch_container; 472 const Extension* extension; 473 if (!GetAppLaunchContainer(profile, app_id, &extension, &launch_container)) 474 return false; 475 476 // If the user doesn't want to open a tab, fail. 477 if (launch_container != extension_misc::LAUNCH_TAB) 478 return false; 479 480 RecordCmdLineAppHistogram(extension->GetType()); 481 482 WebContents* app_tab = chrome::OpenApplication(chrome::AppLaunchParams( 483 profile, extension, extension_misc::LAUNCH_TAB, NEW_FOREGROUND_TAB)); 484 return (app_tab != NULL); 485} 486 487bool StartupBrowserCreatorImpl::OpenApplicationWindow( 488 Profile* profile, 489 content::WebContents** out_app_contents) { 490 // Set |out_app_contents| to NULL early on (just in case). 491 if (out_app_contents) 492 *out_app_contents = NULL; 493 494 std::string url_string, app_id; 495 if (!IsAppLaunch(&url_string, &app_id)) 496 return false; 497 498 // This can fail if the app_id is invalid. It can also fail if the 499 // extension is external, and has not yet been installed. 500 // TODO(skerner): Do something reasonable here. Pop up a warning panel? 501 // Open an URL to the gallery page of the extension id? 502 if (!app_id.empty()) { 503 extension_misc::LaunchContainer launch_container; 504 const Extension* extension; 505 if (!GetAppLaunchContainer(profile, app_id, &extension, &launch_container)) 506 return false; 507 508 // TODO(skerner): Could pass in |extension| and |launch_container|, 509 // and avoid calling GetAppLaunchContainer() both here and in 510 // OpenApplicationTab(). 511 512 if (launch_container == extension_misc::LAUNCH_TAB) 513 return false; 514 515 RecordCmdLineAppHistogram(extension->GetType()); 516 517 chrome::AppLaunchParams params(profile, extension, 518 launch_container, NEW_WINDOW); 519 params.command_line = &command_line_; 520 params.current_directory = cur_dir_; 521 WebContents* tab_in_app_window = chrome::OpenApplication(params); 522 523 if (out_app_contents) 524 *out_app_contents = tab_in_app_window; 525 526 // Platform apps fire off a launch event which may or may not open a window. 527 return (tab_in_app_window != NULL || extension->is_platform_app()); 528 } 529 530 if (url_string.empty()) 531 return false; 532 533#if defined(OS_WIN) // Fix up Windows shortcuts. 534 ReplaceSubstringsAfterOffset(&url_string, 0, "\\x", "%"); 535#endif 536 GURL url(url_string); 537 538 // Restrict allowed URLs for --app switch. 539 if (!url.is_empty() && url.is_valid()) { 540 ChildProcessSecurityPolicy* policy = 541 ChildProcessSecurityPolicy::GetInstance(); 542 if (policy->IsWebSafeScheme(url.scheme()) || 543 url.SchemeIs(chrome::kFileScheme)) { 544 const extensions::Extension* extension = 545 profile->GetExtensionService()->GetInstalledApp(url); 546 if (extension) { 547 RecordCmdLineAppHistogram(extension->GetType()); 548 } else { 549 AppLauncherHandler::RecordAppLaunchType( 550 extension_misc::APP_LAUNCH_CMD_LINE_APP_LEGACY, 551 extensions::Manifest::TYPE_HOSTED_APP); 552 } 553 554 gfx::Rect override_bounds; 555 ExtractOptionalAppWindowSize(&override_bounds); 556 557 WebContents* app_tab = chrome::OpenAppShortcutWindow(profile, 558 url, 559 override_bounds); 560 561 if (out_app_contents) 562 *out_app_contents = app_tab; 563 564 return (app_tab != NULL); 565 } 566 } 567 return false; 568} 569 570void StartupBrowserCreatorImpl::ProcessLaunchURLs( 571 bool process_startup, 572 const std::vector<GURL>& urls_to_open, 573 chrome::HostDesktopType desktop_type) { 574 // If we're starting up in "background mode" (no open browser window) then 575 // don't open any browser windows, unless kAutoLaunchAtStartup is also 576 // specified. 577 if (process_startup && 578 command_line_.HasSwitch(switches::kNoStartupWindow) && 579 !command_line_.HasSwitch(switches::kAutoLaunchAtStartup)) { 580 return; 581 } 582 583// TODO(tapted): Move this to startup_browser_creator_win.cc after refactor. 584#if defined(OS_WIN) 585 if (base::win::GetVersion() >= base::win::VERSION_WIN8) { 586 // See if there are apps for this profile that should be launched on startup 587 // due to a switch from Metro mode. 588 apps::HandleAppLaunchForMetroRestart(profile_); 589 } 590#endif 591 592 if (process_startup && ProcessStartupURLs(urls_to_open, desktop_type)) { 593 // ProcessStartupURLs processed the urls, nothing else to do. 594 return; 595 } 596 597 chrome::startup::IsProcessStartup is_process_startup = process_startup ? 598 chrome::startup::IS_PROCESS_STARTUP : 599 chrome::startup::IS_NOT_PROCESS_STARTUP; 600 if (!process_startup) { 601 // Even if we're not starting a new process, this may conceptually be 602 // "startup" for the user and so should be handled in a similar way. Eg., 603 // Chrome may have been running in the background due to an app with a 604 // background page being installed, or running with only an app window 605 // displayed. 606 SessionService* service = SessionServiceFactory::GetForProfile(profile_); 607 if (service && service->ShouldNewWindowStartSession()) { 608 // Restore the last session if any. 609 if (!HasPendingUncleanExit(profile_) && 610 service->RestoreIfNecessary(urls_to_open)) { 611 return; 612 } 613 // Open user-specified URLs like pinned tabs and startup tabs. 614 Browser* browser = ProcessSpecifiedURLs(urls_to_open, desktop_type); 615 if (browser) { 616 AddInfoBarsIfNecessary(browser, is_process_startup); 617 return; 618 } 619 } 620 } 621 622 // Session startup didn't occur, open the urls. 623 Browser* browser = NULL; 624 std::vector<GURL> adjust_urls = urls_to_open; 625 if (adjust_urls.empty()) { 626 AddStartupURLs(&adjust_urls); 627 if (StartupBrowserCreatorImpl::OpenStartupURLsInExistingBrowser( 628 profile_, adjust_urls)) 629 return; 630 } else if (!command_line_.HasSwitch(switches::kOpenInNewWindow)) { 631 // Always open a list of urls in a window on the native desktop. 632 browser = chrome::FindTabbedBrowser(profile_, false, 633 chrome::HOST_DESKTOP_TYPE_NATIVE); 634 } 635 // This will launch a browser; prevent session restore. 636 StartupBrowserCreator::in_synchronous_profile_launch_ = true; 637 browser = OpenURLsInBrowser(browser, process_startup, adjust_urls, 638 desktop_type); 639 StartupBrowserCreator::in_synchronous_profile_launch_ = false; 640 AddInfoBarsIfNecessary(browser, is_process_startup); 641} 642 643bool StartupBrowserCreatorImpl::ProcessStartupURLs( 644 const std::vector<GURL>& urls_to_open, 645 chrome::HostDesktopType desktop_type) { 646 VLOG(1) << "StartupBrowserCreatorImpl::ProcessStartupURLs"; 647 SessionStartupPref pref = 648 StartupBrowserCreator::GetSessionStartupPref(command_line_, profile_); 649 if (pref.type == SessionStartupPref::LAST) 650 VLOG(1) << "Pref: last"; 651 else if (pref.type == SessionStartupPref::URLS) 652 VLOG(1) << "Pref: urls"; 653 else if (pref.type == SessionStartupPref::DEFAULT) 654 VLOG(1) << "Pref: default"; 655 656 apps::AppRestoreService* restore_service = 657 apps::AppRestoreServiceFactory::GetForProfile(profile_); 658 // NULL in incognito mode. 659 if (restore_service) { 660 restore_service->HandleStartup(apps::AppRestoreService::ShouldRestoreApps( 661 StartupBrowserCreator::WasRestarted())); 662 } 663 664 if (pref.type == SessionStartupPref::LAST) { 665 if (profile_->GetLastSessionExitType() == Profile::EXIT_CRASHED && 666 !command_line_.HasSwitch(switches::kRestoreLastSession)) { 667 // The last session crashed. It's possible automatically loading the 668 // page will trigger another crash, locking the user out of chrome. 669 // To avoid this, don't restore on startup but instead show the crashed 670 // infobar. 671 VLOG(1) << "Unclean exit; not processing"; 672 return false; 673 } 674 675 uint32 restore_behavior = SessionRestore::SYNCHRONOUS; 676 if (browser_defaults::kAlwaysCreateTabbedBrowserOnSessionRestore || 677 CommandLine::ForCurrentProcess()->HasSwitch( 678 switches::kCreateBrowserOnStartupForTests)) { 679 restore_behavior |= SessionRestore::ALWAYS_CREATE_TABBED_BROWSER; 680 } 681 682#if defined(OS_MACOSX) 683 // On Mac, when restoring a session with no windows, suppress the creation 684 // of a new window in the case where the system is launching Chrome via a 685 // login item or Lion's resume feature. 686 if (base::mac::WasLaunchedAsLoginOrResumeItem()) { 687 restore_behavior = restore_behavior & 688 ~SessionRestore::ALWAYS_CREATE_TABBED_BROWSER; 689 } 690#endif 691 692 // Pause the StartupTimer. Since the restore here is synchronous, we can 693 // keep these two metrics (browser startup time and session restore time) 694 // separate. 695 performance_monitor::StartupTimer::PauseTimer(); 696 697 // The startup code only executes for browsers launched in desktop mode. 698 // i.e. HOST_DESKTOP_TYPE_NATIVE. Ash should never get here. 699 Browser* browser = SessionRestore::RestoreSession( 700 profile_, NULL, desktop_type, restore_behavior, 701 urls_to_open); 702 703 performance_monitor::StartupTimer::UnpauseTimer(); 704 705 AddInfoBarsIfNecessary(browser, chrome::startup::IS_PROCESS_STARTUP); 706 return true; 707 } 708 709 Browser* browser = ProcessSpecifiedURLs(urls_to_open, desktop_type); 710 if (!browser) 711 return false; 712 713 AddInfoBarsIfNecessary(browser, chrome::startup::IS_PROCESS_STARTUP); 714 715 // Session restore may occur if the startup preference is "last" or if the 716 // crash infobar is displayed. Otherwise, it's safe for the DOM storage system 717 // to start deleting leftover data. 718 if (pref.type != SessionStartupPref::LAST && 719 !HasPendingUncleanExit(profile_)) { 720 content::BrowserContext::GetDefaultStoragePartition(profile_)-> 721 GetDOMStorageContext()->StartScavengingUnusedSessionStorage(); 722 } 723 724 return true; 725} 726 727Browser* StartupBrowserCreatorImpl::ProcessSpecifiedURLs( 728 const std::vector<GURL>& urls_to_open, 729 chrome::HostDesktopType desktop_type) { 730 SessionStartupPref pref = 731 StartupBrowserCreator::GetSessionStartupPref(command_line_, profile_); 732 StartupTabs tabs; 733 // Pinned tabs should not be displayed when chrome is launched in incognito 734 // mode. Also, no pages should be opened automatically if the session 735 // crashed. Otherwise it might trigger another crash, locking the user out of 736 // chrome. The crash infobar is shown in this case. 737 if (!IncognitoModePrefs::ShouldLaunchIncognito(command_line_, 738 profile_->GetPrefs()) && 739 !HasPendingUncleanExit(profile_)) { 740 tabs = PinnedTabCodec::ReadPinnedTabs(profile_); 741 } 742 743 RecordAppLaunches(profile_, urls_to_open, tabs); 744 745 if (!urls_to_open.empty()) { 746 // If urls were specified on the command line, use them. 747 UrlsToTabs(urls_to_open, &tabs); 748 } else if (pref.type == SessionStartupPref::DEFAULT || 749 (is_first_run_ && 750 browser_creator_ && !browser_creator_->first_run_tabs_.empty())) { 751 std::vector<GURL> urls; 752 AddStartupURLs(&urls); 753 UrlsToTabs(urls, &tabs); 754 } else if (pref.type == SessionStartupPref::URLS && !pref.urls.empty() && 755 !HasPendingUncleanExit(profile_)) { 756 // Only use the set of urls specified in preferences if nothing was 757 // specified on the command line. Filter out any urls that are to be 758 // restored by virtue of having been previously pinned. 759 AddUniqueURLs(pref.urls, &tabs); 760 } else if (pref.type == SessionStartupPref::HOMEPAGE) { 761 // If 'homepage' selected, either by the user or by a policy, we should 762 // have migrated them to another value. 763 NOTREACHED() << "SessionStartupPref has deprecated type HOMEPAGE"; 764 } 765 766 if (pref.type != SessionStartupPref::LAST && 767 SyncPromoUI::ShouldShowSyncPromoAtStartup(profile_, is_first_run_)) { 768 AddSyncPromoTab(profile_, &tabs); 769 } 770 771 if (tabs.empty()) 772 return NULL; 773 774 Browser* browser = OpenTabsInBrowser(NULL, true, tabs, desktop_type); 775 return browser; 776} 777 778void StartupBrowserCreatorImpl::AddUniqueURLs(const std::vector<GURL>& urls, 779 StartupTabs* tabs) { 780 size_t num_existing_tabs = tabs->size(); 781 for (size_t i = 0; i < urls.size(); ++i) { 782 bool in_tabs = false; 783 for (size_t j = 0; j < num_existing_tabs; ++j) { 784 if (urls[i] == (*tabs)[j].url) { 785 in_tabs = true; 786 break; 787 } 788 } 789 if (!in_tabs) { 790 StartupTab tab; 791 tab.is_pinned = false; 792 tab.url = urls[i]; 793 tabs->push_back(tab); 794 } 795 } 796} 797 798Browser* StartupBrowserCreatorImpl::OpenURLsInBrowser( 799 Browser* browser, 800 bool process_startup, 801 const std::vector<GURL>& urls, 802 chrome::HostDesktopType desktop_type) { 803 StartupTabs tabs; 804 UrlsToTabs(urls, &tabs); 805 return OpenTabsInBrowser(browser, process_startup, tabs, desktop_type); 806} 807 808Browser* StartupBrowserCreatorImpl::OpenTabsInBrowser( 809 Browser* browser, 810 bool process_startup, 811 const StartupTabs& tabs, 812 chrome::HostDesktopType desktop_type) { 813 DCHECK(!tabs.empty()); 814 815 // If we don't yet have a profile, try to use the one we're given from 816 // |browser|. While we may not end up actually using |browser| (since it 817 // could be a popup window), we can at least use the profile. 818 if (!profile_ && browser) 819 profile_ = browser->profile(); 820 821 if (!browser || !browser->is_type_tabbed()) { 822 browser = new Browser(Browser::CreateParams(profile_, desktop_type)); 823 } else { 824#if defined(TOOLKIT_GTK) 825 // Setting the time of the last action on the window here allows us to steal 826 // focus, which is what the user wants when opening a new tab in an existing 827 // browser window. 828 gtk_util::SetWMLastUserActionTime(browser->window()->GetNativeWindow()); 829#endif 830 } 831 832 // In kiosk mode, we want to always be fullscreen, so switch to that now. 833 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode)) 834 chrome::ToggleFullscreenMode(browser); 835 836 bool first_tab = true; 837 ProtocolHandlerRegistry* registry = profile_ ? 838 ProtocolHandlerRegistryFactory::GetForProfile(profile_) : NULL; 839 for (size_t i = 0; i < tabs.size(); ++i) { 840 // We skip URLs that we'd have to launch an external protocol handler for. 841 // This avoids us getting into an infinite loop asking ourselves to open 842 // a URL, should the handler be (incorrectly) configured to be us. Anyone 843 // asking us to open such a URL should really ask the handler directly. 844 bool handled_by_chrome = ProfileIOData::IsHandledURL(tabs[i].url) || 845 (registry && registry->IsHandledProtocol(tabs[i].url.scheme())); 846 if (!process_startup && !handled_by_chrome) 847 continue; 848 849 int add_types = first_tab ? TabStripModel::ADD_ACTIVE : 850 TabStripModel::ADD_NONE; 851 add_types |= TabStripModel::ADD_FORCE_INDEX; 852 if (tabs[i].is_pinned) 853 add_types |= TabStripModel::ADD_PINNED; 854 855 chrome::NavigateParams params(browser, tabs[i].url, 856 content::PAGE_TRANSITION_AUTO_TOPLEVEL); 857 params.disposition = first_tab ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB; 858 params.tabstrip_add_types = add_types; 859 params.extension_app_id = tabs[i].app_id; 860 861#if defined(ENABLE_RLZ) 862 if (process_startup && 863 google_util::IsGoogleHomePageUrl(tabs[i].url.spec())) { 864 params.extra_headers = RLZTracker::GetAccessPointHttpHeader( 865 RLZTracker::CHROME_HOME_PAGE); 866 } 867#endif 868 869 chrome::Navigate(¶ms); 870 871 first_tab = false; 872 } 873 if (!browser->tab_strip_model()->GetActiveWebContents()) { 874 // TODO: this is a work around for 110909. Figure out why it's needed. 875 if (!browser->tab_strip_model()->count()) 876 chrome::AddBlankTabAt(browser, -1, true); 877 else 878 browser->tab_strip_model()->ActivateTabAt(0, false); 879 } 880 881 // The default behavior is to show the window, as expressed by the default 882 // value of StartupBrowserCreated::show_main_browser_window_. If this was set 883 // to true ahead of this place, it means another task must have been spawned 884 // to take care of that. 885 if (!browser_creator_ || browser_creator_->show_main_browser_window()) 886 browser->window()->Show(); 887 888 return browser; 889} 890 891void StartupBrowserCreatorImpl::AddInfoBarsIfNecessary( 892 Browser* browser, 893 chrome::startup::IsProcessStartup is_process_startup) { 894 if (!browser || !profile_ || browser->tab_strip_model()->count() == 0) 895 return; 896 897 if (HasPendingUncleanExit(browser->profile())) 898 SessionCrashedInfoBarDelegate::Create(browser); 899 900 // The below info bars are only added to the first profile which is launched. 901 // Other profiles might be restoring the browsing sessions asynchronously, 902 // so we cannot add the info bars to the focused tabs here. 903 if (is_process_startup == chrome::startup::IS_PROCESS_STARTUP) { 904 chrome::ShowBadFlagsPrompt(browser); 905 if (!command_line_.HasSwitch(switches::kTestType)) { 906 GoogleApiKeysInfoBarDelegate::Create( 907 InfoBarService::FromWebContents( 908 browser->tab_strip_model()->GetActiveWebContents())); 909 910 // TODO(phajdan.jr): Always enable after migrating bots: 911 // http://crbug.com/170262 . 912 chrome::ObsoleteOSInfoBarDelegate::Create( 913 InfoBarService::FromWebContents( 914 browser->tab_strip_model()->GetActiveWebContents())); 915 } 916 917 if (browser_defaults::kOSSupportsOtherBrowsers && 918 !command_line_.HasSwitch(switches::kNoDefaultBrowserCheck)) { 919 // Generally, the default browser prompt should not be shown on first 920 // run. However, when the set-as-default dialog has been suppressed, we 921 // need to allow it. 922 if ((!is_first_run_ || 923 (browser_creator_ && 924 browser_creator_->is_default_browser_dialog_suppressed())) && 925 !chrome::ShowAutolaunchPrompt(browser)) { 926 chrome::ShowDefaultBrowserPrompt(profile_, 927 browser->host_desktop_type()); 928 } 929 } 930 } 931} 932 933 934void StartupBrowserCreatorImpl::AddStartupURLs( 935 std::vector<GURL>* startup_urls) const { 936 // If we have urls specified by the first run master preferences use them 937 // and nothing else. 938 if (browser_creator_ && startup_urls->empty()) { 939 if (!browser_creator_->first_run_tabs_.empty()) { 940 std::vector<GURL>::iterator it = 941 browser_creator_->first_run_tabs_.begin(); 942 while (it != browser_creator_->first_run_tabs_.end()) { 943 // Replace magic names for the actual urls. 944 if (it->host() == "new_tab_page") { 945 startup_urls->push_back(GURL(chrome::kChromeUINewTabURL)); 946 } else if (it->host() == "welcome_page") { 947 startup_urls->push_back(internals::GetWelcomePageURL()); 948 } else { 949 startup_urls->push_back(*it); 950 } 951 ++it; 952 } 953 browser_creator_->first_run_tabs_.clear(); 954 } 955 } 956 957 // Otherwise open at least the new tab page (and the welcome page, if this 958 // is the first time the browser is being started), or the set of URLs 959 // specified on the command line. 960 if (startup_urls->empty()) { 961 startup_urls->push_back(GURL(chrome::kChromeUINewTabURL)); 962 if (first_run::ShouldShowWelcomePage()) 963 startup_urls->push_back(internals::GetWelcomePageURL()); 964 } 965} 966 967#if !defined(OS_WIN) 968// static 969bool StartupBrowserCreatorImpl::OpenStartupURLsInExistingBrowser( 970 Profile* profile, 971 const std::vector<GURL>& startup_urls) { 972 return false; 973} 974#endif 975