browser_init.cc revision 201ade2fbba22bfb27ae029f4d23fca6ded109a0
1// Copyright (c) 2010 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/browser_init.h" 6 7#include <algorithm> // For max(). 8 9#include "app/l10n_util.h" 10#include "app/resource_bundle.h" 11#include "base/environment.h" 12#include "base/event_recorder.h" 13#include "base/file_path.h" 14#include "base/metrics/histogram.h" 15#include "base/path_service.h" 16#include "base/scoped_ptr.h" 17#include "base/string_number_conversions.h" 18#include "base/string_split.h" 19#include "base/thread_restrictions.h" 20#include "base/utf_string_conversions.h" 21#include "chrome/browser/automation/automation_provider.h" 22#include "chrome/browser/automation/automation_provider_list.h" 23#include "chrome/browser/automation/chrome_frame_automation_provider.h" 24#include "chrome/browser/automation/testing_automation_provider.h" 25#include "chrome/browser/browser_list.h" 26#include "chrome/browser/browser_process.h" 27#include "chrome/browser/browser_thread.h" 28#include "chrome/browser/browser_window.h" 29#include "chrome/browser/child_process_security_policy.h" 30#include "chrome/browser/defaults.h" 31#include "chrome/browser/extensions/extension_creator.h" 32#include "chrome/browser/extensions/extensions_service.h" 33#include "chrome/browser/extensions/pack_extension_job.h" 34#include "chrome/browser/first_run/first_run.h" 35#include "chrome/browser/net/predictor_api.h" 36#include "chrome/browser/net/url_fixer_upper.h" 37#include "chrome/browser/notifications/desktop_notification_service.h" 38#include "chrome/browser/prefs/pref_service.h" 39#include "chrome/browser/prefs/session_startup_pref.h" 40#include "chrome/browser/printing/cloud_print/cloud_print_proxy_service.h" 41#include "chrome/browser/profile.h" 42#include "chrome/browser/renderer_host/render_process_host.h" 43#include "chrome/browser/search_engines/template_url.h" 44#include "chrome/browser/search_engines/template_url_model.h" 45#include "chrome/browser/sessions/session_restore.h" 46#include "chrome/browser/sessions/session_service.h" 47#include "chrome/browser/shell_integration.h" 48#include "chrome/browser/tab_contents/infobar_delegate.h" 49#include "chrome/browser/tab_contents/navigation_controller.h" 50#include "chrome/browser/tab_contents/tab_contents.h" 51#include "chrome/browser/tab_contents/tab_contents_view.h" 52#include "chrome/browser/tab_contents_wrapper.h" 53#include "chrome/browser/tabs/pinned_tab_codec.h" 54#include "chrome/browser/tabs/tab_strip_model.h" 55#include "chrome/browser/ui/browser_navigator.h" 56#include "chrome/common/chrome_constants.h" 57#include "chrome/common/chrome_paths.h" 58#include "chrome/common/chrome_switches.h" 59#include "chrome/common/pref_names.h" 60#include "chrome/common/result_codes.h" 61#include "chrome/common/url_constants.h" 62#include "chrome/installer/util/browser_distribution.h" 63#include "grit/chromium_strings.h" 64#include "grit/generated_resources.h" 65#include "grit/locale_settings.h" 66#include "grit/theme_resources.h" 67#include "net/base/net_util.h" 68#include "net/url_request/url_request.h" 69#include "webkit/glue/webkit_glue.h" 70 71#if defined(OS_MACOSX) 72#include "chrome/browser/cocoa/keystone_infobar.h" 73#endif 74 75#if defined(OS_WIN) 76#include "app/win_util.h" 77#endif 78 79#if defined(TOOLKIT_GTK) 80#include "chrome/browser/gtk/gtk_util.h" 81#endif 82 83#if defined(OS_CHROMEOS) 84#include "chrome/browser/chromeos/cros/cros_library.h" 85#include "chrome/browser/chromeos/cros/mount_library.h" 86#include "chrome/browser/chromeos/cros/network_library.h" 87#include "chrome/browser/chromeos/customization_document.h" 88#include "chrome/browser/chromeos/gview_request_interceptor.h" 89#include "chrome/browser/chromeos/low_battery_observer.h" 90#include "chrome/browser/chromeos/network_message_observer.h" 91#include "chrome/browser/chromeos/network_state_notifier.h" 92#include "chrome/browser/chromeos/system_key_event_listener.h" 93#include "chrome/browser/chromeos/update_observer.h" 94#include "chrome/browser/chromeos/usb_mount_observer.h" 95#include "chrome/browser/chromeos/wm_message_listener.h" 96#include "chrome/browser/chromeos/wm_overview_controller.h" 97#include "chrome/browser/dom_ui/mediaplayer_ui.h" 98#endif 99 100#if defined(HAVE_XINPUT2) 101#include "views/focus/accelerator_handler.h" 102#endif 103 104namespace { 105 106class SetAsDefaultBrowserTask : public Task { 107 public: 108 SetAsDefaultBrowserTask() { } 109 virtual void Run() { 110 ShellIntegration::SetAsDefaultBrowser(); 111 } 112 113 private: 114 DISALLOW_COPY_AND_ASSIGN(SetAsDefaultBrowserTask); 115}; 116 117// The delegate for the infobar shown when Chrome is not the default browser. 118class DefaultBrowserInfoBarDelegate : public ConfirmInfoBarDelegate { 119 public: 120 explicit DefaultBrowserInfoBarDelegate(TabContents* contents) 121 : ConfirmInfoBarDelegate(contents), 122 profile_(contents->profile()), 123 action_taken_(false), 124 should_expire_(false), 125 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { 126 // We want the info-bar to stick-around for few seconds and then be hidden 127 // on the next navigation after that. 128 MessageLoop::current()->PostDelayedTask(FROM_HERE, 129 method_factory_.NewRunnableMethod( 130 &DefaultBrowserInfoBarDelegate::Expire), 131 8000); // 8 seconds. 132 } 133 134 virtual bool ShouldExpire( 135 const NavigationController::LoadCommittedDetails& details) const { 136 return should_expire_; 137 } 138 139 // Overridden from ConfirmInfoBarDelegate: 140 virtual void InfoBarClosed() { 141 if (!action_taken_) 142 UMA_HISTOGRAM_COUNTS("DefaultBrowserWarning.Ignored", 1); 143 delete this; 144 } 145 146 virtual string16 GetMessageText() const { 147 return l10n_util::GetStringUTF16(IDS_DEFAULT_BROWSER_INFOBAR_SHORT_TEXT); 148 } 149 150 virtual SkBitmap* GetIcon() const { 151 return ResourceBundle::GetSharedInstance().GetBitmapNamed( 152 IDR_PRODUCT_ICON_32); 153 } 154 155 virtual int GetButtons() const { 156 return BUTTON_OK | BUTTON_CANCEL | BUTTON_OK_DEFAULT; 157 } 158 159 virtual string16 GetButtonLabel(InfoBarButton button) const { 160 return button == BUTTON_OK ? 161 l10n_util::GetStringUTF16(IDS_SET_AS_DEFAULT_INFOBAR_BUTTON_LABEL) : 162 l10n_util::GetStringUTF16(IDS_DONT_ASK_AGAIN_INFOBAR_BUTTON_LABEL); 163 } 164 165 virtual bool NeedElevation(InfoBarButton button) const { 166 return button == BUTTON_OK; 167 } 168 169 virtual bool Accept() { 170 action_taken_ = true; 171 UMA_HISTOGRAM_COUNTS("DefaultBrowserWarning.SetAsDefault", 1); 172 g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE, 173 new SetAsDefaultBrowserTask()); 174 return true; 175 } 176 177 virtual bool Cancel() { 178 action_taken_ = true; 179 UMA_HISTOGRAM_COUNTS("DefaultBrowserWarning.DontSetAsDefault", 1); 180 // User clicked "Don't ask me again", remember that. 181 profile_->GetPrefs()->SetBoolean(prefs::kCheckDefaultBrowser, false); 182 return true; 183 } 184 185 void Expire() { 186 should_expire_ = true; 187 } 188 189 private: 190 // The Profile that we restore sessions from. 191 Profile* profile_; 192 193 // Whether the user clicked one of the buttons. 194 bool action_taken_; 195 196 // Whether the info-bar should be dismissed on the next navigation. 197 bool should_expire_; 198 199 // Used to delay the expiration of the info-bar. 200 ScopedRunnableMethodFactory<DefaultBrowserInfoBarDelegate> method_factory_; 201 202 DISALLOW_COPY_AND_ASSIGN(DefaultBrowserInfoBarDelegate); 203}; 204 205class NotifyNotDefaultBrowserTask : public Task { 206 public: 207 NotifyNotDefaultBrowserTask() { } 208 209 virtual void Run() { 210 Browser* browser = BrowserList::GetLastActive(); 211 if (!browser) { 212 // Reached during ui tests. 213 return; 214 } 215 TabContents* tab = browser->GetSelectedTabContents(); 216 // Don't show the info-bar if there are already info-bars showing. 217 // In ChromeBot tests, there might be a race. This line appears to get 218 // called during shutdown and |tab| can be NULL. 219 if (!tab || tab->infobar_delegate_count() > 0) 220 return; 221 tab->AddInfoBar(new DefaultBrowserInfoBarDelegate(tab)); 222 } 223 224 private: 225 DISALLOW_COPY_AND_ASSIGN(NotifyNotDefaultBrowserTask); 226}; 227 228class CheckDefaultBrowserTask : public Task { 229 public: 230 CheckDefaultBrowserTask() { 231 } 232 233 virtual void Run() { 234 if (ShellIntegration::IsDefaultBrowser()) 235 return; 236#if defined(OS_WIN) 237 if (!BrowserDistribution::GetDistribution()->CanSetAsDefault()) 238 return; 239#endif 240 241 BrowserThread::PostTask( 242 BrowserThread::UI, FROM_HERE, new NotifyNotDefaultBrowserTask()); 243 } 244 245 private: 246 DISALLOW_COPY_AND_ASSIGN(CheckDefaultBrowserTask); 247}; 248 249// A delegate for the InfoBar shown when the previous session has crashed. The 250// bar deletes itself automatically after it is closed. 251class SessionCrashedInfoBarDelegate : public ConfirmInfoBarDelegate { 252 public: 253 explicit SessionCrashedInfoBarDelegate(TabContents* contents) 254 : ConfirmInfoBarDelegate(contents), 255 profile_(contents->profile()) { 256 } 257 258 // Overridden from ConfirmInfoBarDelegate: 259 virtual void InfoBarClosed() { 260 delete this; 261 } 262 virtual string16 GetMessageText() const { 263 return l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_VIEW_MESSAGE); 264 } 265 virtual SkBitmap* GetIcon() const { 266 return ResourceBundle::GetSharedInstance().GetBitmapNamed( 267 IDR_INFOBAR_RESTORE_SESSION); 268 } 269 virtual int GetButtons() const { return BUTTON_OK; } 270 virtual string16 GetButtonLabel(InfoBarButton button) const { 271 return l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_VIEW_RESTORE_BUTTON); 272 } 273 virtual bool Accept() { 274 // Restore the session. 275 SessionRestore::RestoreSession(profile_, NULL, true, false, 276 std::vector<GURL>()); 277 return true; 278 } 279 280 private: 281 // The Profile that we restore sessions from. 282 Profile* profile_; 283 284 DISALLOW_COPY_AND_ASSIGN(SessionCrashedInfoBarDelegate); 285}; 286 287SessionStartupPref GetSessionStartupPref(const CommandLine& command_line, 288 Profile* profile) { 289 SessionStartupPref pref = SessionStartupPref::GetStartupPref(profile); 290 if (command_line.HasSwitch(switches::kRestoreLastSession)) 291 pref.type = SessionStartupPref::LAST; 292 if (command_line.HasSwitch(switches::kIncognito) && 293 pref.type == SessionStartupPref::LAST) { 294 // We don't store session information when incognito. If the user has 295 // chosen to restore last session and launched incognito, fallback to 296 // default launch behavior. 297 pref.type = SessionStartupPref::DEFAULT; 298 } 299 return pref; 300} 301 302enum LaunchMode { 303 LM_TO_BE_DECIDED = 0, // Possibly direct launch or via a shortcut. 304 LM_AS_WEBAPP, // Launched as a installed web application. 305 LM_WITH_URLS, // Launched with urls in the cmd line. 306 LM_SHORTCUT_NONE, // Not launched from a shortcut. 307 LM_SHORTCUT_NONAME, // Launched from shortcut but no name available. 308 LM_SHORTCUT_UNKNOWN, // Launched from user-defined shortcut. 309 LM_SHORTCUT_QUICKLAUNCH, // Launched from the quick launch bar. 310 LM_SHORTCUT_DESKTOP, // Launched from a desktop shortcut. 311 LM_SHORTCUT_STARTMENU, // Launched from start menu. 312 LM_LINUX_MAC_BEOS // Other OS buckets start here. 313}; 314 315#if defined(OS_WIN) 316// Undocumented flag in the startup info structure tells us what shortcut was 317// used to launch the browser. See http://www.catch22.net/tuts/undoc01 for 318// more information. Confirmed to work on XP, Vista and Win7. 319LaunchMode GetLaunchShortcutKind() { 320 STARTUPINFOW si = { sizeof(si) }; 321 GetStartupInfoW(&si); 322 if (si.dwFlags & 0x800) { 323 if (!si.lpTitle) 324 return LM_SHORTCUT_NONAME; 325 std::wstring shortcut(si.lpTitle); 326 // The windows quick launch path is not localized. 327 if (shortcut.find(L"\\Quick Launch\\") != std::wstring::npos) 328 return LM_SHORTCUT_QUICKLAUNCH; 329 scoped_ptr<base::Environment> env(base::Environment::Create()); 330 std::string appdata_path; 331 env->GetVar("USERPROFILE", &appdata_path); 332 if (!appdata_path.empty() && 333 shortcut.find(ASCIIToWide(appdata_path)) != std::wstring::npos) 334 return LM_SHORTCUT_DESKTOP; 335 return LM_SHORTCUT_UNKNOWN; 336 } 337 return LM_SHORTCUT_NONE; 338} 339#else 340// TODO(cpu): Port to other platforms. 341LaunchMode GetLaunchShortcutKind() { 342 return LM_LINUX_MAC_BEOS; 343} 344#endif 345 346// Log in a histogram the frequency of launching by the different methods. See 347// LaunchMode enum for the actual values of the buckets. 348void RecordLaunchModeHistogram(LaunchMode mode) { 349 int bucket = (mode == LM_TO_BE_DECIDED) ? GetLaunchShortcutKind() : mode; 350 UMA_HISTOGRAM_COUNTS_100("Launch.Modes", bucket); 351} 352 353static bool in_startup = false; 354 355GURL GetWelcomePageURL() { 356 std::string welcome_url = l10n_util::GetStringUTF8(IDS_WELCOME_PAGE_URL); 357 return GURL(welcome_url); 358} 359 360void UrlsToTabs(const std::vector<GURL>& urls, 361 std::vector<BrowserInit::LaunchWithProfile::Tab>* tabs) { 362 for (size_t i = 0; i < urls.size(); ++i) { 363 BrowserInit::LaunchWithProfile::Tab tab; 364 tab.is_pinned = false; 365 tab.url = urls[i]; 366 tabs->push_back(tab); 367 } 368} 369 370} // namespace 371 372BrowserInit::BrowserInit() {} 373 374BrowserInit::~BrowserInit() {} 375 376void BrowserInit::AddFirstRunTab(const GURL& url) { 377 first_run_tabs_.push_back(url); 378} 379 380// static 381bool BrowserInit::InProcessStartup() { 382 return in_startup; 383} 384 385bool BrowserInit::LaunchBrowser(const CommandLine& command_line, 386 Profile* profile, 387 const FilePath& cur_dir, 388 bool process_startup, 389 int* return_code) { 390 in_startup = process_startup; 391 DCHECK(profile); 392#if defined(OS_CHROMEOS) 393 if (process_startup) { 394 // NetworkStateNotifier has to be initialized before Launching browser 395 // because the page load can happen in parallel to this UI thread 396 // and IO thread may access the NetworkStateNotifier. 397 chromeos::CrosLibrary::Get()->GetNetworkLibrary() 398 ->AddNetworkManagerObserver(chromeos::NetworkStateNotifier::Get()); 399 } 400#endif 401 402 // Continue with the off-the-record profile from here on if --incognito 403 if (command_line.HasSwitch(switches::kIncognito)) 404 profile = profile->GetOffTheRecordProfile(); 405 406 BrowserInit::LaunchWithProfile lwp(cur_dir, command_line, this); 407 bool launched = lwp.Launch(profile, process_startup); 408 in_startup = false; 409 410 if (!launched) { 411 LOG(ERROR) << "launch error"; 412 if (return_code) 413 *return_code = ResultCodes::INVALID_CMDLINE_URL; 414 return false; 415 } 416 417#if defined(OS_CHROMEOS) 418 // Create the WmMessageListener so that it can listen for messages regardless 419 // of what window has focus. 420 chromeos::WmMessageListener::instance(); 421 422 // Create the SystemKeyEventListener so it can listen for system keyboard 423 // messages regardless of focus. 424 chromeos::SystemKeyEventListener::instance(); 425 426 // Create the WmOverviewController so it can register with the listener. 427 chromeos::WmOverviewController::instance(); 428 429 // Install the GView request interceptor that will redirect requests 430 // of compatible documents (PDF, etc) to the GView document viewer. 431 const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess(); 432 if (parsed_command_line.HasSwitch(switches::kEnableGView)) { 433 chromeos::GViewRequestInterceptor::GetGViewRequestInterceptor(); 434 } 435 if (process_startup) { 436 // TODO(dhg): Try to make this just USBMountObserver::Get()->set_profile 437 // and have the constructor take care of everything else. 438 chromeos::MountLibrary* lib = 439 chromeos::CrosLibrary::Get()->GetMountLibrary(); 440 chromeos::USBMountObserver* observe = chromeos::USBMountObserver::Get(); 441 lib->AddObserver(observe); 442 observe->ScanForDevices(lib); 443 // Connect the chromeos notifications 444 445 // This observer is a singleton. It is never deleted but the pointer is kept 446 // in a global so that it isn't reported as a leak. 447 static chromeos::LowBatteryObserver* low_battery_observer = 448 new chromeos::LowBatteryObserver(profile); 449 chromeos::CrosLibrary::Get()->GetPowerLibrary()->AddObserver( 450 low_battery_observer); 451 452 static chromeos::UpdateObserver* update_observer = 453 new chromeos::UpdateObserver(profile); 454 chromeos::CrosLibrary::Get()->GetUpdateLibrary()->AddObserver( 455 update_observer); 456 457 static chromeos::NetworkMessageObserver* network_message_observer = 458 new chromeos::NetworkMessageObserver(profile); 459 chromeos::CrosLibrary::Get()->GetNetworkLibrary() 460 ->AddNetworkManagerObserver(network_message_observer); 461 chromeos::CrosLibrary::Get()->GetNetworkLibrary() 462 ->AddCellularDataPlanObserver(network_message_observer); 463 } 464#endif 465 return true; 466} 467 468// Tab ------------------------------------------------------------------------ 469 470BrowserInit::LaunchWithProfile::Tab::Tab() : is_app(false), is_pinned(true) {} 471 472BrowserInit::LaunchWithProfile::Tab::~Tab() {} 473 474// LaunchWithProfile ---------------------------------------------------------- 475 476BrowserInit::LaunchWithProfile::LaunchWithProfile( 477 const FilePath& cur_dir, 478 const CommandLine& command_line) 479 : cur_dir_(cur_dir), 480 command_line_(command_line), 481 profile_(NULL), 482 browser_init_(NULL) { 483} 484 485BrowserInit::LaunchWithProfile::LaunchWithProfile( 486 const FilePath& cur_dir, 487 const CommandLine& command_line, 488 BrowserInit* browser_init) 489 : cur_dir_(cur_dir), 490 command_line_(command_line), 491 profile_(NULL), 492 browser_init_(browser_init) { 493} 494 495BrowserInit::LaunchWithProfile::~LaunchWithProfile() { 496} 497 498bool BrowserInit::LaunchWithProfile::Launch(Profile* profile, 499 bool process_startup) { 500 DCHECK(profile); 501 profile_ = profile; 502 503 if (command_line_.HasSwitch(switches::kDnsLogDetails)) 504 chrome_browser_net::EnablePredictorDetailedLog(true); 505 if (command_line_.HasSwitch(switches::kDnsPrefetchDisable)) 506 chrome_browser_net::EnablePredictor(false); 507 508 if (command_line_.HasSwitch(switches::kDumpHistogramsOnExit)) 509 base::StatisticsRecorder::set_dump_on_exit(true); 510 511 if (command_line_.HasSwitch(switches::kRemoteShellPort)) { 512 std::string port_str = 513 command_line_.GetSwitchValueASCII(switches::kRemoteShellPort); 514 int64 port; 515 if (base::StringToInt64(port_str, &port) && port > 0 && port < 65535) 516 g_browser_process->InitDebuggerWrapper(static_cast<int>(port), false); 517 else 518 DLOG(WARNING) << "Invalid remote shell port number " << port; 519 } else if (command_line_.HasSwitch(switches::kRemoteDebuggingPort)) { 520 std::string port_str = 521 command_line_.GetSwitchValueASCII(switches::kRemoteDebuggingPort); 522 int64 port; 523 if (base::StringToInt64(port_str, &port) && port > 0 && port < 65535) 524 g_browser_process->InitDebuggerWrapper(static_cast<int>(port), true); 525 else 526 DLOG(WARNING) << "Invalid http debugger port number " << port; 527 } 528 529 if (command_line_.HasSwitch(switches::kUserAgent)) { 530 webkit_glue::SetUserAgent(command_line_.GetSwitchValueASCII( 531 switches::kUserAgent)); 532 } 533 534 // Open the required browser windows and tabs. 535 // First, see if we're being run as an application window. 536 if (!OpenApplicationWindow(profile)) { 537 std::vector<GURL> urls_to_open = GetURLsFromCommandLine(profile_); 538 RecordLaunchModeHistogram(urls_to_open.empty()? 539 LM_TO_BE_DECIDED : LM_WITH_URLS); 540 ProcessLaunchURLs(process_startup, urls_to_open); 541 542 // If this is an app launch, but we didn't open an app window, it may 543 // be an app tab. 544 std::string app_id; 545 if (IsAppLaunch(NULL, &app_id) && !app_id.empty()) { 546 // TODO(erikkay): This could fail if |app_id| is invalid (the app was 547 // uninstalled). We may want to show some reasonable error here. 548 Browser::OpenApplication(profile, app_id, NULL); 549 } 550 551 if (process_startup) { 552 if (browser_defaults::kOSSupportsOtherBrowsers && 553 !command_line_.HasSwitch(switches::kNoDefaultBrowserCheck)) { 554 // Check whether we are the default browser. 555 CheckDefaultBrowser(profile); 556 } 557#if defined(OS_MACOSX) 558 // Check whether the auto-update system needs to be promoted from user 559 // to system. 560 KeystoneInfoBar::PromotionInfoBar(profile); 561#endif 562 } 563 } else { 564 RecordLaunchModeHistogram(LM_AS_WEBAPP); 565 } 566 567#if defined(OS_WIN) 568 // Print the selected page if the command line switch exists. Note that the 569 // current selected tab would be the page which will be printed. 570 if (command_line_.HasSwitch(switches::kPrint)) { 571 Browser* browser = BrowserList::GetLastActive(); 572 browser->Print(); 573 } 574#endif 575 576 // If we're recording or playing back, startup the EventRecorder now 577 // unless otherwise specified. 578 if (!command_line_.HasSwitch(switches::kNoEvents)) { 579 FilePath script_path; 580 PathService::Get(chrome::FILE_RECORDED_SCRIPT, &script_path); 581 582 bool record_mode = command_line_.HasSwitch(switches::kRecordMode); 583 bool playback_mode = command_line_.HasSwitch(switches::kPlaybackMode); 584 585 if (record_mode && chrome::kRecordModeEnabled) 586 base::EventRecorder::current()->StartRecording(script_path); 587 if (playback_mode) 588 base::EventRecorder::current()->StartPlayback(script_path); 589 } 590 591#if defined(OS_WIN) 592 if (process_startup) 593 ShellIntegration::MigrateChromiumShortcuts(); 594#endif // defined(OS_WIN) 595 596 return true; 597} 598 599bool BrowserInit::LaunchWithProfile::IsAppLaunch(std::string* app_url, 600 std::string* app_id) { 601 if (command_line_.HasSwitch(switches::kApp)) { 602 if (app_url) 603 *app_url = command_line_.GetSwitchValueASCII(switches::kApp); 604 return true; 605 } 606 if (command_line_.HasSwitch(switches::kAppId)) { 607 if (app_id) 608 *app_id = command_line_.GetSwitchValueASCII(switches::kAppId); 609 return true; 610 } 611 return false; 612} 613 614bool BrowserInit::LaunchWithProfile::OpenApplicationWindow(Profile* profile) { 615 std::string url_string, app_id; 616 if (!IsAppLaunch(&url_string, &app_id)) 617 return false; 618 619 // This can fail if the app_id is invalid. It can also fail if the 620 // extension is external, and has not yet been installed. 621 // TODO(skerner): Do something reasonable here. Pop up a warning panel? 622 // Open an URL to the gallery page of the extension id? 623 if (!app_id.empty()) { 624 ExtensionsService* extensions_service = profile->GetExtensionsService(); 625 const Extension* extension = 626 extensions_service->GetExtensionById(app_id, false); 627 628 // The extension with id |app_id| may have been uninstalled. 629 if (!extension) 630 return false; 631 632 // Look at preferences to find the right launch container. If no 633 // preference is set, launch as a window. 634 extension_misc::LaunchContainer launch_container = 635 extensions_service->extension_prefs()->GetLaunchContainer( 636 extension, ExtensionPrefs::LAUNCH_WINDOW); 637 638 TabContents* app_window = Browser::OpenApplication( 639 profile, extension, launch_container, NULL); 640 return (app_window != NULL); 641 } 642 643 if (url_string.empty()) 644 return false; 645 646#if defined(OS_WIN) // Fix up Windows shortcuts. 647 ReplaceSubstringsAfterOffset(&url_string, 0, "\\x", "%"); 648#endif 649 GURL url(url_string); 650 651 // Restrict allowed URLs for --app switch. 652 if (!url.is_empty() && url.is_valid()) { 653 ChildProcessSecurityPolicy *policy = 654 ChildProcessSecurityPolicy::GetInstance(); 655 if (policy->IsWebSafeScheme(url.scheme()) || 656 url.SchemeIs(chrome::kFileScheme)) { 657 TabContents* app_tab = Browser::OpenAppShortcutWindow( 658 profile, 659 url, 660 true); // Update app info. 661 return (app_tab != NULL); 662 } 663 } 664 return false; 665} 666 667void BrowserInit::LaunchWithProfile::ProcessLaunchURLs( 668 bool process_startup, 669 const std::vector<GURL>& urls_to_open) { 670 // If we're starting up in "background mode" (no open browser window) then 671 // don't open any browser windows. 672 if (process_startup && command_line_.HasSwitch(switches::kNoStartupWindow)) 673 return; 674 675 if (process_startup && ProcessStartupURLs(urls_to_open)) { 676 // ProcessStartupURLs processed the urls, nothing else to do. 677 return; 678 } 679 680 if (!process_startup && 681 (profile_->GetSessionService() && 682 profile_->GetSessionService()->RestoreIfNecessary(urls_to_open))) { 683 // We're already running and session restore wanted to run. This can happen 684 // at various points, such as if there is only an app window running and the 685 // user double clicked the chrome icon. Return so we don't open the urls. 686 return; 687 } 688 689 // Session restore didn't occur, open the urls. 690 691 Browser* browser = NULL; 692 std::vector<GURL> adjust_urls = urls_to_open; 693 if (adjust_urls.empty()) 694 AddStartupURLs(&adjust_urls); 695 else if (!command_line_.HasSwitch(switches::kOpenInNewWindow)) 696 browser = BrowserList::GetLastActiveWithProfile(profile_); 697 698 OpenURLsInBrowser(browser, process_startup, adjust_urls); 699} 700 701bool BrowserInit::LaunchWithProfile::ProcessStartupURLs( 702 const std::vector<GURL>& urls_to_open) { 703 SessionStartupPref pref = GetSessionStartupPref(command_line_, profile_); 704 if (command_line_.HasSwitch(switches::kTestingChannelID) && 705 !command_line_.HasSwitch(switches::kRestoreLastSession) && 706 browser_defaults::kDefaultSessionStartupType != 707 SessionStartupPref::DEFAULT) { 708 // When we have non DEFAULT session start type, then we won't open up a 709 // fresh session. But none of the tests are written with this in mind, so 710 // we explicitly ignore it during testing. 711 return false; 712 } 713 714 if (pref.type == SessionStartupPref::LAST) { 715 if (!profile_->DidLastSessionExitCleanly() && 716 !command_line_.HasSwitch(switches::kRestoreLastSession)) { 717 // The last session crashed. It's possible automatically loading the 718 // page will trigger another crash, locking the user out of chrome. 719 // To avoid this, don't restore on startup but instead show the crashed 720 // infobar. 721 return false; 722 } 723 SessionRestore::RestoreSessionSynchronously(profile_, urls_to_open); 724 return true; 725 } 726 727 std::vector<Tab> tabs = PinnedTabCodec::ReadPinnedTabs(profile_); 728 729 if (!urls_to_open.empty()) { 730 // If urls were specified on the command line, use them. 731 UrlsToTabs(urls_to_open, &tabs); 732 } else if (pref.type == SessionStartupPref::URLS && !pref.urls.empty()) { 733 // Only use the set of urls specified in preferences if nothing was 734 // specified on the command line. 735 UrlsToTabs(pref.urls, &tabs); 736 } 737 738 if (tabs.empty()) 739 return false; 740 741 OpenTabsInBrowser(NULL, true, tabs); 742 return true; 743} 744 745Browser* BrowserInit::LaunchWithProfile::OpenURLsInBrowser( 746 Browser* browser, 747 bool process_startup, 748 const std::vector<GURL>& urls) { 749 std::vector<Tab> tabs; 750 UrlsToTabs(urls, &tabs); 751 return OpenTabsInBrowser(browser, process_startup, tabs); 752} 753 754Browser* BrowserInit::LaunchWithProfile::OpenTabsInBrowser( 755 Browser* browser, 756 bool process_startup, 757 const std::vector<Tab>& tabs) { 758 DCHECK(!tabs.empty()); 759 // If we don't yet have a profile, try to use the one we're given from 760 // |browser|. While we may not end up actually using |browser| (since it 761 // could be a popup window), we can at least use the profile. 762 if (!profile_ && browser) 763 profile_ = browser->profile(); 764 765 if (!browser || browser->type() != Browser::TYPE_NORMAL) { 766 browser = Browser::Create(profile_); 767 } else { 768#if defined(TOOLKIT_GTK) 769 // Setting the time of the last action on the window here allows us to steal 770 // focus, which is what the user wants when opening a new tab in an existing 771 // browser window. 772 gtk_util::SetWMLastUserActionTime(browser->window()->GetNativeHandle()); 773#endif 774 } 775 776#if !defined(OS_MACOSX) 777 // In kiosk mode, we want to always be fullscreen, so switch to that now. 778 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode)) 779 browser->ToggleFullscreenMode(); 780#endif 781 782 bool first_tab = true; 783 for (size_t i = 0; i < tabs.size(); ++i) { 784 // We skip URLs that we'd have to launch an external protocol handler for. 785 // This avoids us getting into an infinite loop asking ourselves to open 786 // a URL, should the handler be (incorrectly) configured to be us. Anyone 787 // asking us to open such a URL should really ask the handler directly. 788 if (!process_startup && !URLRequest::IsHandledURL(tabs[i].url)) 789 continue; 790 791 int add_types = first_tab ? TabStripModel::ADD_SELECTED : 792 TabStripModel::ADD_NONE; 793 add_types |= TabStripModel::ADD_FORCE_INDEX; 794 if (tabs[i].is_pinned) 795 add_types |= TabStripModel::ADD_PINNED; 796 int index = browser->GetIndexForInsertionDuringRestore(i); 797 798 browser::NavigateParams params(browser, tabs[i].url, 799 PageTransition::START_PAGE); 800 params.disposition = first_tab ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB; 801 params.tabstrip_index = index; 802 params.tabstrip_add_types = add_types; 803 params.extension_app_id = tabs[i].app_id; 804 browser::Navigate(¶ms); 805 806 if (profile_ && first_tab && process_startup) { 807 AddCrashedInfoBarIfNecessary(params.target_contents->tab_contents()); 808 AddBadFlagsInfoBarIfNecessary(params.target_contents->tab_contents()); 809 } 810 811 first_tab = false; 812 } 813 browser->window()->Show(); 814 // TODO(jcampan): http://crbug.com/8123 we should not need to set the initial 815 // focus explicitly. 816 browser->GetSelectedTabContents()->view()->SetInitialFocus(); 817 818 return browser; 819} 820 821void BrowserInit::LaunchWithProfile::AddCrashedInfoBarIfNecessary( 822 TabContents* tab) { 823 // Assume that if the user is launching incognito they were previously 824 // running incognito so that we have nothing to restore from. 825 if (!profile_->DidLastSessionExitCleanly() && 826 !profile_->IsOffTheRecord()) { 827 // The last session didn't exit cleanly. Show an infobar to the user 828 // so that they can restore if they want. The delegate deletes itself when 829 // it is closed. 830 tab->AddInfoBar(new SessionCrashedInfoBarDelegate(tab)); 831 } 832} 833 834void BrowserInit::LaunchWithProfile::AddBadFlagsInfoBarIfNecessary( 835 TabContents* tab) { 836 // Unsupported flags for which to display a warning that "stability and 837 // security will suffer". 838 static const char* kBadFlags[] = { 839 // All imply disabling the sandbox. 840 switches::kSingleProcess, 841 switches::kNoSandbox, 842 switches::kInProcessWebGL, 843 NULL 844 }; 845 846 const char* bad_flag = NULL; 847 for (const char** flag = kBadFlags; *flag; ++flag) { 848 if (command_line_.HasSwitch(*flag)) { 849 bad_flag = *flag; 850 break; 851 } 852 } 853 854 if (bad_flag) { 855 tab->AddInfoBar(new SimpleAlertInfoBarDelegate(tab, 856 l10n_util::GetStringFUTF16(IDS_BAD_FLAGS_WARNING_MESSAGE, 857 UTF8ToUTF16(std::string("--") + bad_flag)), 858 NULL, false)); 859 } 860} 861 862std::vector<GURL> BrowserInit::LaunchWithProfile::GetURLsFromCommandLine( 863 Profile* profile) { 864 std::vector<GURL> urls; 865 const std::vector<CommandLine::StringType>& params = command_line_.args(); 866 867 for (size_t i = 0; i < params.size(); ++i) { 868 FilePath param = FilePath(params[i]); 869 // Handle Vista way of searching - "? <search-term>" 870 if (param.value().find(FILE_PATH_LITERAL("? ")) == 0) { 871 const TemplateURL* default_provider = 872 profile->GetTemplateURLModel()->GetDefaultSearchProvider(); 873 if (!default_provider || !default_provider->url()) { 874 // No search provider available. Just treat this as regular URL. 875 urls.push_back(URLFixerUpper::FixupRelativeFile(cur_dir_, param)); 876 continue; 877 } 878 const TemplateURLRef* search_url = default_provider->url(); 879 DCHECK(search_url->SupportsReplacement()); 880 std::wstring search_term = param.ToWStringHack().substr(2); 881 urls.push_back(GURL(search_url->ReplaceSearchTerms( 882 *default_provider, search_term, 883 TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, std::wstring()))); 884 } else { 885 // This will create a file URL or a regular URL. 886 // This call can (in rare circumstances) block the UI thread. 887 // Allow it until this bug is fixed. 888 // http://code.google.com/p/chromium/issues/detail?id=60641 889 GURL url; 890 { 891 base::ThreadRestrictions::ScopedAllowIO allow_io; 892 url = URLFixerUpper::FixupRelativeFile(cur_dir_, param); 893 } 894 // Exclude dangerous schemes. 895 if (url.is_valid()) { 896 ChildProcessSecurityPolicy *policy = 897 ChildProcessSecurityPolicy::GetInstance(); 898 if (policy->IsWebSafeScheme(url.scheme()) || 899 url.SchemeIs(chrome::kFileScheme) || 900#if defined(OS_CHROMEOS) 901 // In ChromeOS, allow a settings page to be specified on the 902 // command line. See ExistingUserController::OnLoginSuccess. 903 (url.spec().find(chrome::kChromeUISettingsURL) == 0) || 904#endif 905 (url.spec().compare(chrome::kAboutBlankURL) == 0)) { 906 urls.push_back(url); 907 } 908 } 909 } 910 } 911 return urls; 912} 913 914void BrowserInit::LaunchWithProfile::AddStartupURLs( 915 std::vector<GURL>* startup_urls) const { 916 // If we have urls specified beforehand (i.e. from command line) use them 917 // and nothing else. 918 if (!startup_urls->empty()) 919 return; 920 // If we have urls specified by the first run master preferences use them 921 // and nothing else. 922 if (browser_init_) { 923 if (!browser_init_->first_run_tabs_.empty()) { 924 std::vector<GURL>::iterator it = browser_init_->first_run_tabs_.begin(); 925 while (it != browser_init_->first_run_tabs_.end()) { 926 // Replace magic names for the actual urls. 927 if (it->host() == "new_tab_page") { 928 startup_urls->push_back(GURL()); 929 } else if (it->host() == "welcome_page") { 930 startup_urls->push_back(GetWelcomePageURL()); 931 } else { 932 startup_urls->push_back(*it); 933 } 934 ++it; 935 } 936 browser_init_->first_run_tabs_.clear(); 937 return; 938 } 939 } 940 941 // Otherwise open at least the new tab page (and the welcome page, if this 942 // is the first time the browser is being started), or the set of URLs 943 // specified on the command line. 944 startup_urls->push_back(GURL()); // New tab page. 945 PrefService* prefs = g_browser_process->local_state(); 946 if (prefs->FindPreference(prefs::kShouldShowWelcomePage) && 947 prefs->GetBoolean(prefs::kShouldShowWelcomePage)) { 948 // Reset the preference so we don't show the welcome page next time. 949 prefs->ClearPref(prefs::kShouldShowWelcomePage); 950 startup_urls->push_back(GetWelcomePageURL()); 951 } 952} 953 954void BrowserInit::LaunchWithProfile::CheckDefaultBrowser(Profile* profile) { 955 // We do not check if we are the default browser if: 956 // - the user said "don't ask me again" on the infobar earlier. 957 // - this is the first launch after the first run flow. 958 if (!profile->GetPrefs()->GetBoolean(prefs::kCheckDefaultBrowser) || 959 FirstRun::IsChromeFirstRun()) { 960 return; 961 } 962 BrowserThread::PostTask( 963 BrowserThread::FILE, FROM_HERE, new CheckDefaultBrowserTask()); 964} 965 966bool BrowserInit::ProcessCmdLineImpl(const CommandLine& command_line, 967 const FilePath& cur_dir, 968 bool process_startup, 969 Profile* profile, 970 int* return_code, 971 BrowserInit* browser_init) { 972 DCHECK(profile); 973 if (process_startup) { 974 if (command_line.HasSwitch(switches::kDisablePromptOnRepost)) 975 NavigationController::DisablePromptOnRepost(); 976 977 // Look for the testing channel ID ONLY during process startup 978 if (command_line.HasSwitch(switches::kTestingChannelID)) { 979 std::string testing_channel_id = command_line.GetSwitchValueASCII( 980 switches::kTestingChannelID); 981 // TODO(sanjeevr) Check if we need to make this a singleton for 982 // compatibility with the old testing code 983 // If there are any extra parameters, we expect each one to generate a 984 // new tab; if there are none then we get one homepage tab. 985 int expected_tab_count = 1; 986 if (command_line.HasSwitch(switches::kNoStartupWindow)) { 987 expected_tab_count = 0; 988 } else if (command_line.HasSwitch(switches::kRestoreLastSession)) { 989 std::string restore_session_value( 990 command_line.GetSwitchValueASCII(switches::kRestoreLastSession)); 991 base::StringToInt(restore_session_value, &expected_tab_count); 992 } else { 993 expected_tab_count = 994 std::max(1, static_cast<int>(command_line.args().size())); 995 } 996 if (!CreateAutomationProvider<TestingAutomationProvider>( 997 testing_channel_id, 998 profile, 999 static_cast<size_t>(expected_tab_count))) 1000 return false; 1001 } 1002 } 1003 1004 bool silent_launch = false; 1005 1006 if (command_line.HasSwitch(switches::kAutomationClientChannelID)) { 1007 std::string automation_channel_id = command_line.GetSwitchValueASCII( 1008 switches::kAutomationClientChannelID); 1009 // If there are any extra parameters, we expect each one to generate a 1010 // new tab; if there are none then we have no tabs 1011 size_t expected_tabs = 1012 std::max(static_cast<int>(command_line.args().size()), 0); 1013 if (expected_tabs == 0) 1014 silent_launch = true; 1015 1016 if (command_line.HasSwitch(switches::kChromeFrame)) { 1017 if (!CreateAutomationProvider<ChromeFrameAutomationProvider>( 1018 automation_channel_id, profile, expected_tabs)) 1019 return false; 1020 } else { 1021 if (!CreateAutomationProvider<AutomationProvider>( 1022 automation_channel_id, profile, expected_tabs)) 1023 return false; 1024 } 1025 } 1026 1027 // If we have been invoked to display a desktop notification on behalf of 1028 // the service process, we do not want to open any browser windows. 1029 if (command_line.HasSwitch(switches::kNotifyCloudPrintTokenExpired)) { 1030 silent_launch = true; 1031 profile->GetCloudPrintProxyService()->ShowTokenExpiredNotification(); 1032 } 1033 1034 if (command_line.HasSwitch(switches::kExplicitlyAllowedPorts)) { 1035 std::string allowed_ports = 1036 command_line.GetSwitchValueASCII(switches::kExplicitlyAllowedPorts); 1037 net::SetExplicitlyAllowedPorts(allowed_ports); 1038 } 1039 1040#if defined(OS_CHROMEOS) 1041 // The browser will be launched after the user logs in. 1042 if (command_line.HasSwitch(switches::kLoginManager) || 1043 command_line.HasSwitch(switches::kLoginPassword)) { 1044 silent_launch = true; 1045 } 1046#endif 1047 1048#if defined(HAVE_XINPUT2) && defined(TOUCH_UI) 1049 // Get a list of pointer-devices that should be treated as touch-devices. 1050 // TODO(sad): Instead of/in addition to getting the list from the 1051 // command-line, query X for a list of touch devices. 1052 std::string touch_devices = 1053 command_line.GetSwitchValueASCII(switches::kTouchDevices); 1054 1055 if (!touch_devices.empty()) { 1056 std::vector<std::string> devs; 1057 std::vector<unsigned int> device_ids; 1058 unsigned int devid; 1059 base::SplitString(touch_devices, ',', &devs); 1060 for (std::vector<std::string>::iterator iter = devs.begin(); 1061 iter != devs.end(); ++iter) { 1062 if (base::StringToInt(*iter, reinterpret_cast<int*>(&devid))) { 1063 device_ids.push_back(devid); 1064 } else { 1065 DLOG(WARNING) << "Invalid touch-device id: " << *iter; 1066 } 1067 } 1068 views::SetTouchDeviceList(device_ids); 1069 } 1070#endif 1071 1072 // If we don't want to launch a new browser window or tab (in the case 1073 // of an automation request), we are done here. 1074 if (!silent_launch) { 1075 return browser_init->LaunchBrowser( 1076 command_line, profile, cur_dir, process_startup, return_code); 1077 } 1078 return true; 1079} 1080 1081template <class AutomationProviderClass> 1082bool BrowserInit::CreateAutomationProvider(const std::string& channel_id, 1083 Profile* profile, 1084 size_t expected_tabs) { 1085 scoped_refptr<AutomationProviderClass> automation = 1086 new AutomationProviderClass(profile); 1087 1088 if (!automation->InitializeChannel(channel_id)) 1089 return false; 1090 automation->SetExpectedTabCount(expected_tabs); 1091 1092 AutomationProviderList* list = 1093 g_browser_process->InitAutomationProviderList(); 1094 DCHECK(list); 1095 list->AddProvider(automation); 1096 1097 return true; 1098} 1099