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