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