// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/ui/browser_init.h" #include <algorithm> // For max(). #include "base/compiler_specific.h" #include "base/environment.h" #include "base/event_recorder.h" #include "base/file_path.h" #include "base/memory/scoped_ptr.h" #include "base/metrics/histogram.h" #include "base/path_service.h" #include "base/string_number_conversions.h" #include "base/string_split.h" #include "base/threading/thread_restrictions.h" #include "base/utf_string_conversions.h" #include "chrome/browser/automation/automation_provider.h" #include "chrome/browser/automation/automation_provider_list.h" #include "chrome/browser/automation/chrome_frame_automation_provider.h" #include "chrome/browser/automation/testing_automation_provider.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/defaults.h" #include "chrome/browser/extensions/extension_creator.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/pack_extension_job.h" #include "chrome/browser/first_run/first_run.h" #include "chrome/browser/net/predictor_api.h" #include "chrome/browser/net/url_fixer_upper.h" #include "chrome/browser/notifications/desktop_notification_service.h" #include "chrome/browser/platform_util.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/prefs/session_startup_pref.h" #include "chrome/browser/printing/cloud_print/cloud_print_proxy_service.h" #include "chrome/browser/printing/print_dialog_cloud.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/search_engines/template_url.h" #include "chrome/browser/search_engines/template_url_model.h" #include "chrome/browser/sessions/session_restore.h" #include "chrome/browser/sessions/session_service.h" #include "chrome/browser/shell_integration.h" #include "chrome/browser/tab_contents/link_infobar_delegate.h" #include "chrome/browser/tab_contents/simple_alert_infobar_delegate.h" #include "chrome/browser/tabs/pinned_tab_codec.h" #include "chrome/browser/tabs/tab_strip_model.h" #include "chrome/browser/ui/browser_list.h" #include "chrome/browser/ui/browser_navigator.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/extension_constants.h" #include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" #include "chrome/installer/util/browser_distribution.h" #include "content/browser/browser_thread.h" #include "content/browser/child_process_security_policy.h" #include "content/browser/renderer_host/render_process_host.h" #include "content/browser/tab_contents/navigation_controller.h" #include "content/browser/tab_contents/tab_contents.h" #include "content/browser/tab_contents/tab_contents_view.h" #include "content/common/result_codes.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "grit/locale_settings.h" #include "grit/theme_resources.h" #include "net/base/net_util.h" #include "net/url_request/url_request.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" #include "webkit/glue/webkit_glue.h" #if defined(OS_MACOSX) #include "chrome/browser/ui/cocoa/keystone_infobar.h" #endif #if defined(TOOLKIT_USES_GTK) #include "chrome/browser/ui/gtk/gtk_util.h" #endif #if defined(OS_CHROMEOS) #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/cros/mount_library.h" #include "chrome/browser/chromeos/cros/network_library.h" #include "chrome/browser/chromeos/customization_document.h" #include "chrome/browser/chromeos/enterprise_extension_observer.h" #include "chrome/browser/chromeos/gview_request_interceptor.h" #include "chrome/browser/chromeos/low_battery_observer.h" #include "chrome/browser/chromeos/network_message_observer.h" #include "chrome/browser/chromeos/network_state_notifier.h" #include "chrome/browser/chromeos/sms_observer.h" #include "chrome/browser/chromeos/update_observer.h" #include "chrome/browser/chromeos/wm_message_listener.h" #include "chrome/browser/chromeos/wm_overview_controller.h" #include "chrome/browser/ui/webui/mediaplayer_ui.h" #endif #if defined(HAVE_XINPUT2) #include "views/focus/accelerator_handler.h" #endif namespace { // SetAsDefaultBrowserTask ---------------------------------------------------- class SetAsDefaultBrowserTask : public Task { public: SetAsDefaultBrowserTask(); virtual ~SetAsDefaultBrowserTask(); private: virtual void Run(); DISALLOW_COPY_AND_ASSIGN(SetAsDefaultBrowserTask); }; SetAsDefaultBrowserTask::SetAsDefaultBrowserTask() { } SetAsDefaultBrowserTask::~SetAsDefaultBrowserTask() { } void SetAsDefaultBrowserTask::Run() { ShellIntegration::SetAsDefaultBrowser(); } // DefaultBrowserInfoBarDelegate ---------------------------------------------- // The delegate for the infobar shown when Chrome is not the default browser. class DefaultBrowserInfoBarDelegate : public ConfirmInfoBarDelegate { public: explicit DefaultBrowserInfoBarDelegate(TabContents* contents); private: virtual ~DefaultBrowserInfoBarDelegate(); void AllowExpiry() { should_expire_ = true; } // ConfirmInfoBarDelegate: virtual bool ShouldExpire( const NavigationController::LoadCommittedDetails& details) const OVERRIDE; virtual void InfoBarClosed() OVERRIDE; virtual SkBitmap* GetIcon() const OVERRIDE; virtual string16 GetMessageText() const OVERRIDE; virtual string16 GetButtonLabel(InfoBarButton button) const OVERRIDE; virtual bool NeedElevation(InfoBarButton button) const OVERRIDE; virtual bool Accept() OVERRIDE; virtual bool Cancel() OVERRIDE; // The Profile that we restore sessions from. Profile* profile_; // Whether the user clicked one of the buttons. bool action_taken_; // Whether the info-bar should be dismissed on the next navigation. bool should_expire_; // Used to delay the expiration of the info-bar. ScopedRunnableMethodFactory<DefaultBrowserInfoBarDelegate> method_factory_; DISALLOW_COPY_AND_ASSIGN(DefaultBrowserInfoBarDelegate); }; DefaultBrowserInfoBarDelegate::DefaultBrowserInfoBarDelegate( TabContents* contents) : ConfirmInfoBarDelegate(contents), profile_(contents->profile()), action_taken_(false), should_expire_(false), ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { // We want the info-bar to stick-around for few seconds and then be hidden // on the next navigation after that. MessageLoop::current()->PostDelayedTask(FROM_HERE, method_factory_.NewRunnableMethod( &DefaultBrowserInfoBarDelegate::AllowExpiry), 8000); // 8 seconds. } DefaultBrowserInfoBarDelegate::~DefaultBrowserInfoBarDelegate() { } bool DefaultBrowserInfoBarDelegate::ShouldExpire( const NavigationController::LoadCommittedDetails& details) const { return should_expire_; } void DefaultBrowserInfoBarDelegate::InfoBarClosed() { if (!action_taken_) UMA_HISTOGRAM_COUNTS("DefaultBrowserWarning.Ignored", 1); delete this; } SkBitmap* DefaultBrowserInfoBarDelegate::GetIcon() const { return ResourceBundle::GetSharedInstance().GetBitmapNamed( IDR_PRODUCT_ICON_32); } string16 DefaultBrowserInfoBarDelegate::GetMessageText() const { return l10n_util::GetStringUTF16(IDS_DEFAULT_BROWSER_INFOBAR_SHORT_TEXT); } string16 DefaultBrowserInfoBarDelegate::GetButtonLabel( InfoBarButton button) const { return l10n_util::GetStringUTF16((button == BUTTON_OK) ? IDS_SET_AS_DEFAULT_INFOBAR_BUTTON_LABEL : IDS_DONT_ASK_AGAIN_INFOBAR_BUTTON_LABEL); } bool DefaultBrowserInfoBarDelegate::NeedElevation(InfoBarButton button) const { return button == BUTTON_OK; } bool DefaultBrowserInfoBarDelegate::Accept() { action_taken_ = true; UMA_HISTOGRAM_COUNTS("DefaultBrowserWarning.SetAsDefault", 1); g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE, new SetAsDefaultBrowserTask()); return true; } bool DefaultBrowserInfoBarDelegate::Cancel() { action_taken_ = true; UMA_HISTOGRAM_COUNTS("DefaultBrowserWarning.DontSetAsDefault", 1); // User clicked "Don't ask me again", remember that. profile_->GetPrefs()->SetBoolean(prefs::kCheckDefaultBrowser, false); return true; } // NotifyNotDefaultBrowserTask ------------------------------------------------ class NotifyNotDefaultBrowserTask : public Task { public: NotifyNotDefaultBrowserTask(); virtual ~NotifyNotDefaultBrowserTask(); private: virtual void Run(); DISALLOW_COPY_AND_ASSIGN(NotifyNotDefaultBrowserTask); }; NotifyNotDefaultBrowserTask::NotifyNotDefaultBrowserTask() { } NotifyNotDefaultBrowserTask::~NotifyNotDefaultBrowserTask() { } void NotifyNotDefaultBrowserTask::Run() { Browser* browser = BrowserList::GetLastActive(); if (!browser) return; // Reached during ui tests. // Don't show the info-bar if there are already info-bars showing. // In ChromeBot tests, there might be a race. This line appears to get // called during shutdown and |tab| can be NULL. TabContents* tab = browser->GetSelectedTabContents(); if (!tab || tab->infobar_count() > 0) return; tab->AddInfoBar(new DefaultBrowserInfoBarDelegate(tab)); } // CheckDefaultBrowserTask ---------------------------------------------------- class CheckDefaultBrowserTask : public Task { public: CheckDefaultBrowserTask(); virtual ~CheckDefaultBrowserTask(); private: virtual void Run(); DISALLOW_COPY_AND_ASSIGN(CheckDefaultBrowserTask); }; CheckDefaultBrowserTask::CheckDefaultBrowserTask() { } CheckDefaultBrowserTask::~CheckDefaultBrowserTask() { } void CheckDefaultBrowserTask::Run() { if (ShellIntegration::IsDefaultBrowser() || !platform_util::CanSetAsDefaultBrowser()) { return; } BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, new NotifyNotDefaultBrowserTask()); } // SessionCrashedInfoBarDelegate ---------------------------------------------- // A delegate for the InfoBar shown when the previous session has crashed. class SessionCrashedInfoBarDelegate : public ConfirmInfoBarDelegate { public: explicit SessionCrashedInfoBarDelegate(TabContents* contents); private: virtual ~SessionCrashedInfoBarDelegate(); // ConfirmInfoBarDelegate: virtual void InfoBarClosed() OVERRIDE; virtual SkBitmap* GetIcon() const OVERRIDE; virtual string16 GetMessageText() const OVERRIDE; virtual int GetButtons() const OVERRIDE; virtual string16 GetButtonLabel(InfoBarButton button) const OVERRIDE; virtual bool Accept() OVERRIDE; // The Profile that we restore sessions from. Profile* profile_; DISALLOW_COPY_AND_ASSIGN(SessionCrashedInfoBarDelegate); }; SessionCrashedInfoBarDelegate::SessionCrashedInfoBarDelegate( TabContents* contents) : ConfirmInfoBarDelegate(contents), profile_(contents->profile()) { } SessionCrashedInfoBarDelegate::~SessionCrashedInfoBarDelegate() { } void SessionCrashedInfoBarDelegate::InfoBarClosed() { delete this; } SkBitmap* SessionCrashedInfoBarDelegate::GetIcon() const { return ResourceBundle::GetSharedInstance().GetBitmapNamed( IDR_INFOBAR_RESTORE_SESSION); } string16 SessionCrashedInfoBarDelegate::GetMessageText() const { return l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_VIEW_MESSAGE); } int SessionCrashedInfoBarDelegate::GetButtons() const { return BUTTON_OK; } string16 SessionCrashedInfoBarDelegate::GetButtonLabel( InfoBarButton button) const { DCHECK_EQ(BUTTON_OK, button); return l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_VIEW_RESTORE_BUTTON); } bool SessionCrashedInfoBarDelegate::Accept() { SessionRestore::RestoreSession(profile_, NULL, true, false, std::vector<GURL>()); return true; } // Utility functions ---------------------------------------------------------- SessionStartupPref GetSessionStartupPref(const CommandLine& command_line, Profile* profile) { SessionStartupPref pref = SessionStartupPref::GetStartupPref(profile); if (command_line.HasSwitch(switches::kRestoreLastSession)) pref.type = SessionStartupPref::LAST; if (command_line.HasSwitch(switches::kIncognito) && pref.type == SessionStartupPref::LAST && profile->GetPrefs()->GetBoolean(prefs::kIncognitoEnabled)) { // We don't store session information when incognito. If the user has // chosen to restore last session and launched incognito, fallback to // default launch behavior. pref.type = SessionStartupPref::DEFAULT; } return pref; } enum LaunchMode { LM_TO_BE_DECIDED = 0, // Possibly direct launch or via a shortcut. LM_AS_WEBAPP, // Launched as a installed web application. LM_WITH_URLS, // Launched with urls in the cmd line. LM_SHORTCUT_NONE, // Not launched from a shortcut. LM_SHORTCUT_NONAME, // Launched from shortcut but no name available. LM_SHORTCUT_UNKNOWN, // Launched from user-defined shortcut. LM_SHORTCUT_QUICKLAUNCH, // Launched from the quick launch bar. LM_SHORTCUT_DESKTOP, // Launched from a desktop shortcut. LM_SHORTCUT_STARTMENU, // Launched from start menu. LM_LINUX_MAC_BEOS // Other OS buckets start here. }; #if defined(OS_WIN) // Undocumented flag in the startup info structure tells us what shortcut was // used to launch the browser. See http://www.catch22.net/tuts/undoc01 for // more information. Confirmed to work on XP, Vista and Win7. LaunchMode GetLaunchShortcutKind() { STARTUPINFOW si = { sizeof(si) }; GetStartupInfoW(&si); if (si.dwFlags & 0x800) { if (!si.lpTitle) return LM_SHORTCUT_NONAME; std::wstring shortcut(si.lpTitle); // The windows quick launch path is not localized. if (shortcut.find(L"\\Quick Launch\\") != std::wstring::npos) return LM_SHORTCUT_QUICKLAUNCH; scoped_ptr<base::Environment> env(base::Environment::Create()); std::string appdata_path; env->GetVar("USERPROFILE", &appdata_path); if (!appdata_path.empty() && shortcut.find(ASCIIToWide(appdata_path)) != std::wstring::npos) return LM_SHORTCUT_DESKTOP; return LM_SHORTCUT_UNKNOWN; } return LM_SHORTCUT_NONE; } #else // TODO(cpu): Port to other platforms. LaunchMode GetLaunchShortcutKind() { return LM_LINUX_MAC_BEOS; } #endif // Log in a histogram the frequency of launching by the different methods. See // LaunchMode enum for the actual values of the buckets. void RecordLaunchModeHistogram(LaunchMode mode) { int bucket = (mode == LM_TO_BE_DECIDED) ? GetLaunchShortcutKind() : mode; UMA_HISTOGRAM_COUNTS_100("Launch.Modes", bucket); } static bool in_startup = false; GURL GetWelcomePageURL() { std::string welcome_url = l10n_util::GetStringUTF8(IDS_WELCOME_PAGE_URL); return GURL(welcome_url); } void UrlsToTabs(const std::vector<GURL>& urls, std::vector<BrowserInit::LaunchWithProfile::Tab>* tabs) { for (size_t i = 0; i < urls.size(); ++i) { BrowserInit::LaunchWithProfile::Tab tab; tab.is_pinned = false; tab.url = urls[i]; tabs->push_back(tab); } } // Return true if the command line option --app-id is used. Set // |out_extension| to the app to open, and |out_launch_container| // to the type of window into which the app should be open. bool GetAppLaunchContainer( Profile* profile, const std::string& app_id, const Extension** out_extension, extension_misc::LaunchContainer* out_launch_container) { ExtensionService* extensions_service = profile->GetExtensionService(); const Extension* extension = extensions_service->GetExtensionById(app_id, false); // The extension with id |app_id| may have been uninstalled. if (!extension) return false; // Look at preferences to find the right launch container. If no // preference is set, launch as a window. extension_misc::LaunchContainer launch_container = extensions_service->extension_prefs()->GetLaunchContainer( extension, ExtensionPrefs::LAUNCH_WINDOW); *out_extension = extension; *out_launch_container = launch_container; return true; } void RecordCmdLineAppHistogram() { UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppLaunchHistogram, extension_misc::APP_LAUNCH_CMD_LINE_APP, extension_misc::APP_LAUNCH_BUCKET_BOUNDARY); } void RecordAppLaunches( Profile* profile, const std::vector<GURL>& cmd_line_urls, const std::vector<BrowserInit::LaunchWithProfile::Tab>& autolaunch_tabs) { ExtensionService* extension_service = profile->GetExtensionService(); DCHECK(extension_service); for (size_t i = 0; i < cmd_line_urls.size(); ++i) { if (extension_service->IsInstalledApp(cmd_line_urls.at(i))) { UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppLaunchHistogram, extension_misc::APP_LAUNCH_CMD_LINE_URL, extension_misc::APP_LAUNCH_BUCKET_BOUNDARY); } } for (size_t i = 0; i < autolaunch_tabs.size(); ++i) { if (extension_service->IsInstalledApp(autolaunch_tabs.at(i).url)) { UMA_HISTOGRAM_ENUMERATION(extension_misc::kAppLaunchHistogram, extension_misc::APP_LAUNCH_AUTOLAUNCH, extension_misc::APP_LAUNCH_BUCKET_BOUNDARY); } } } } // namespace // BrowserInit ---------------------------------------------------------------- BrowserInit::BrowserInit() {} BrowserInit::~BrowserInit() {} void BrowserInit::AddFirstRunTab(const GURL& url) { first_run_tabs_.push_back(url); } // static bool BrowserInit::InProcessStartup() { return in_startup; } bool BrowserInit::LaunchBrowser(const CommandLine& command_line, Profile* profile, const FilePath& cur_dir, bool process_startup, int* return_code) { in_startup = process_startup; DCHECK(profile); #if defined(OS_CHROMEOS) if (process_startup) { // NetworkStateNotifier has to be initialized before Launching browser // because the page load can happen in parallel to this UI thread // and IO thread may access the NetworkStateNotifier. chromeos::CrosLibrary::Get()->GetNetworkLibrary() ->AddNetworkManagerObserver( chromeos::NetworkStateNotifier::GetInstance()); } #endif // Continue with the incognito profile from here on if --incognito if (command_line.HasSwitch(switches::kIncognito) && profile->GetPrefs()->GetBoolean(prefs::kIncognitoEnabled)) { profile = profile->GetOffTheRecordProfile(); } BrowserInit::LaunchWithProfile lwp(cur_dir, command_line, this); std::vector<GURL> urls_to_launch = BrowserInit::GetURLsFromCommandLine( command_line, cur_dir, profile); bool launched = lwp.Launch(profile, urls_to_launch, process_startup); in_startup = false; if (!launched) { LOG(ERROR) << "launch error"; if (return_code) *return_code = ResultCodes::INVALID_CMDLINE_URL; return false; } #if defined(OS_CHROMEOS) // Initialize Chrome OS preferences like touch pad sensitivity. For the // preferences to work in the guest mode, the initialization has to be // done after |profile| is switched to the incognito profile (which // is actually GuestSessionProfile in the guest mode). See the // GetOffTheRecordProfile() call above. profile->InitChromeOSPreferences(); // Create the WmMessageListener so that it can listen for messages regardless // of what window has focus. chromeos::WmMessageListener::GetInstance(); // Create the WmOverviewController so it can register with the listener. chromeos::WmOverviewController::GetInstance(); // Install the GView request interceptor that will redirect requests // of compatible documents (PDF, etc) to the GView document viewer. const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess(); if (parsed_command_line.HasSwitch(switches::kEnableGView)) { chromeos::GViewRequestInterceptor::GetInstance(); } if (process_startup) { // This observer is a singleton. It is never deleted but the pointer is kept // in a static so that it isn't reported as a leak. static chromeos::LowBatteryObserver* low_battery_observer = new chromeos::LowBatteryObserver(profile); chromeos::CrosLibrary::Get()->GetPowerLibrary()->AddObserver( low_battery_observer); static chromeos::UpdateObserver* update_observer = new chromeos::UpdateObserver(profile); chromeos::CrosLibrary::Get()->GetUpdateLibrary()->AddObserver( update_observer); static chromeos::NetworkMessageObserver* network_message_observer = new chromeos::NetworkMessageObserver(profile); chromeos::CrosLibrary::Get()->GetNetworkLibrary() ->AddNetworkManagerObserver(network_message_observer); chromeos::CrosLibrary::Get()->GetNetworkLibrary() ->AddCellularDataPlanObserver(network_message_observer); chromeos::CrosLibrary::Get()->GetNetworkLibrary() ->AddUserActionObserver(network_message_observer); static chromeos::SmsObserver* sms_observer = new chromeos::SmsObserver(profile); chromeos::CrosLibrary::Get()->GetNetworkLibrary() ->AddNetworkManagerObserver(sms_observer); profile->SetupChromeOSEnterpriseExtensionObserver(); } #endif return true; } // BrowserInit::LaunchWithProfile::Tab ---------------------------------------- BrowserInit::LaunchWithProfile::Tab::Tab() : is_app(false), is_pinned(true) {} BrowserInit::LaunchWithProfile::Tab::~Tab() {} // BrowserInit::LaunchWithProfile --------------------------------------------- BrowserInit::LaunchWithProfile::LaunchWithProfile( const FilePath& cur_dir, const CommandLine& command_line) : cur_dir_(cur_dir), command_line_(command_line), profile_(NULL), browser_init_(NULL) { } BrowserInit::LaunchWithProfile::LaunchWithProfile( const FilePath& cur_dir, const CommandLine& command_line, BrowserInit* browser_init) : cur_dir_(cur_dir), command_line_(command_line), profile_(NULL), browser_init_(browser_init) { } BrowserInit::LaunchWithProfile::~LaunchWithProfile() { } bool BrowserInit::LaunchWithProfile::Launch( Profile* profile, const std::vector<GURL>& urls_to_open, bool process_startup) { DCHECK(profile); profile_ = profile; if (command_line_.HasSwitch(switches::kDnsLogDetails)) chrome_browser_net::EnablePredictorDetailedLog(true); if (command_line_.HasSwitch(switches::kDnsPrefetchDisable)) chrome_browser_net::EnablePredictor(false); if (command_line_.HasSwitch(switches::kDumpHistogramsOnExit)) base::StatisticsRecorder::set_dump_on_exit(true); if (command_line_.HasSwitch(switches::kRemoteShellPort)) { std::string port_str = command_line_.GetSwitchValueASCII(switches::kRemoteShellPort); int64 port; if (base::StringToInt64(port_str, &port) && port > 0 && port < 65535) { g_browser_process->InitDevToolsLegacyProtocolHandler( static_cast<int>(port)); } else { DLOG(WARNING) << "Invalid remote shell port number " << port; } } else if (command_line_.HasSwitch(switches::kRemoteDebuggingPort)) { std::string port_str = command_line_.GetSwitchValueASCII(switches::kRemoteDebuggingPort); int64 port; if (base::StringToInt64(port_str, &port) && port > 0 && port < 65535) { g_browser_process->InitDevToolsHttpProtocolHandler( "127.0.0.1", static_cast<int>(port), ""); } else { DLOG(WARNING) << "Invalid http debugger port number " << port; } } if (command_line_.HasSwitch(switches::kUserAgent)) { webkit_glue::SetUserAgent(command_line_.GetSwitchValueASCII( switches::kUserAgent)); } // Open the required browser windows and tabs. First, see if // we're being run as an application window. If so, the user // opened an app shortcut. Don't restore tabs or open initial // URLs in that case. The user should see the window as an app, // not as chrome. if (OpenApplicationWindow(profile)) { RecordLaunchModeHistogram(LM_AS_WEBAPP); } else { RecordLaunchModeHistogram(urls_to_open.empty()? LM_TO_BE_DECIDED : LM_WITH_URLS); ProcessLaunchURLs(process_startup, urls_to_open); // If this is an app launch, but we didn't open an app window, it may // be an app tab. OpenApplicationTab(profile); if (process_startup) { if (browser_defaults::kOSSupportsOtherBrowsers && !command_line_.HasSwitch(switches::kNoDefaultBrowserCheck)) { // Check whether we are the default browser. CheckDefaultBrowser(profile); } #if defined(OS_MACOSX) // Check whether the auto-update system needs to be promoted from user // to system. KeystoneInfoBar::PromotionInfoBar(profile); #endif } } #if defined(OS_WIN) // Print the selected page if the command line switch exists. Note that the // current selected tab would be the page which will be printed. if (command_line_.HasSwitch(switches::kPrint)) { Browser* browser = BrowserList::GetLastActive(); browser->Print(); } #endif // If we're recording or playing back, startup the EventRecorder now // unless otherwise specified. if (!command_line_.HasSwitch(switches::kNoEvents)) { FilePath script_path; PathService::Get(chrome::FILE_RECORDED_SCRIPT, &script_path); bool record_mode = command_line_.HasSwitch(switches::kRecordMode); bool playback_mode = command_line_.HasSwitch(switches::kPlaybackMode); if (record_mode && chrome::kRecordModeEnabled) base::EventRecorder::current()->StartRecording(script_path); if (playback_mode) base::EventRecorder::current()->StartPlayback(script_path); } #if defined(OS_WIN) if (process_startup) ShellIntegration::MigrateChromiumShortcuts(); #endif // defined(OS_WIN) return true; } bool BrowserInit::LaunchWithProfile::IsAppLaunch(std::string* app_url, std::string* app_id) { if (command_line_.HasSwitch(switches::kApp)) { if (app_url) *app_url = command_line_.GetSwitchValueASCII(switches::kApp); return true; } if (command_line_.HasSwitch(switches::kAppId)) { if (app_id) *app_id = command_line_.GetSwitchValueASCII(switches::kAppId); return true; } return false; } bool BrowserInit::LaunchWithProfile::OpenApplicationTab(Profile* profile) { std::string app_id; // App shortcuts to URLs always open in an app window. Because this // function will open an app that should be in a tab, there is no need // to look at the app URL. OpenApplicationWindow() will open app url // shortcuts. if (!IsAppLaunch(NULL, &app_id) || app_id.empty()) return false; extension_misc::LaunchContainer launch_container; const Extension* extension; if (!GetAppLaunchContainer(profile, app_id, &extension, &launch_container)) return false; // If the user doesn't want to open a tab, fail. if (launch_container != extension_misc::LAUNCH_TAB) return false; RecordCmdLineAppHistogram(); TabContents* app_tab = Browser::OpenApplicationTab(profile, extension, NULL); return (app_tab != NULL); } bool BrowserInit::LaunchWithProfile::OpenApplicationWindow(Profile* profile) { std::string url_string, app_id; if (!IsAppLaunch(&url_string, &app_id)) return false; // This can fail if the app_id is invalid. It can also fail if the // extension is external, and has not yet been installed. // TODO(skerner): Do something reasonable here. Pop up a warning panel? // Open an URL to the gallery page of the extension id? if (!app_id.empty()) { extension_misc::LaunchContainer launch_container; const Extension* extension; if (!GetAppLaunchContainer(profile, app_id, &extension, &launch_container)) return false; // TODO(skerner): Could pass in |extension| and |launch_container|, // and avoid calling GetAppLaunchContainer() both here and in // OpenApplicationTab(). if (launch_container == extension_misc::LAUNCH_TAB) return false; RecordCmdLineAppHistogram(); TabContents* tab_in_app_window = Browser::OpenApplication( profile, extension, launch_container, NULL); return (tab_in_app_window != NULL); } if (url_string.empty()) return false; #if defined(OS_WIN) // Fix up Windows shortcuts. ReplaceSubstringsAfterOffset(&url_string, 0, "\\x", "%"); #endif GURL url(url_string); // Restrict allowed URLs for --app switch. if (!url.is_empty() && url.is_valid()) { ChildProcessSecurityPolicy *policy = ChildProcessSecurityPolicy::GetInstance(); if (policy->IsWebSafeScheme(url.scheme()) || url.SchemeIs(chrome::kFileScheme)) { if (profile->GetExtensionService()->IsInstalledApp(url)) { RecordCmdLineAppHistogram(); } else { UMA_HISTOGRAM_ENUMERATION( extension_misc::kAppLaunchHistogram, extension_misc::APP_LAUNCH_CMD_LINE_APP_LEGACY, extension_misc::APP_LAUNCH_BUCKET_BOUNDARY); } TabContents* app_tab = Browser::OpenAppShortcutWindow( profile, url, true); // Update app info. return (app_tab != NULL); } } return false; } void BrowserInit::LaunchWithProfile::ProcessLaunchURLs( bool process_startup, const std::vector<GURL>& urls_to_open) { // If we're starting up in "background mode" (no open browser window) then // don't open any browser windows. if (process_startup && command_line_.HasSwitch(switches::kNoStartupWindow)) return; if (process_startup && ProcessStartupURLs(urls_to_open)) { // ProcessStartupURLs processed the urls, nothing else to do. return; } if (!process_startup && (profile_->GetSessionService() && profile_->GetSessionService()->RestoreIfNecessary(urls_to_open))) { // We're already running and session restore wanted to run. This can happen // at various points, such as if there is only an app window running and the // user double clicked the chrome icon. Return so we don't open the urls. return; } // Session restore didn't occur, open the urls. Browser* browser = NULL; std::vector<GURL> adjust_urls = urls_to_open; if (adjust_urls.empty()) AddStartupURLs(&adjust_urls); else if (!command_line_.HasSwitch(switches::kOpenInNewWindow)) browser = BrowserList::GetLastActiveWithProfile(profile_); browser = OpenURLsInBrowser(browser, process_startup, adjust_urls); if (process_startup) AddInfoBarsIfNecessary(browser); } bool BrowserInit::LaunchWithProfile::ProcessStartupURLs( const std::vector<GURL>& urls_to_open) { SessionStartupPref pref = GetSessionStartupPref(command_line_, profile_); if (command_line_.HasSwitch(switches::kTestingChannelID) && !command_line_.HasSwitch(switches::kRestoreLastSession) && browser_defaults::kDefaultSessionStartupType != SessionStartupPref::DEFAULT) { // When we have non DEFAULT session start type, then we won't open up a // fresh session. But none of the tests are written with this in mind, so // we explicitly ignore it during testing. return false; } if (pref.type == SessionStartupPref::LAST) { if (!profile_->DidLastSessionExitCleanly() && !command_line_.HasSwitch(switches::kRestoreLastSession)) { // The last session crashed. It's possible automatically loading the // page will trigger another crash, locking the user out of chrome. // To avoid this, don't restore on startup but instead show the crashed // infobar. return false; } Browser* browser = SessionRestore::RestoreSessionSynchronously(profile_, urls_to_open); AddInfoBarsIfNecessary(browser); return true; } std::vector<Tab> tabs = PinnedTabCodec::ReadPinnedTabs(profile_); RecordAppLaunches(profile_, urls_to_open, tabs); if (!urls_to_open.empty()) { // If urls were specified on the command line, use them. UrlsToTabs(urls_to_open, &tabs); } else if (pref.type == SessionStartupPref::URLS && !pref.urls.empty()) { // Only use the set of urls specified in preferences if nothing was // specified on the command line. Filter out any urls that are to be // restored by virtue of having been previously pinned. AddUniqueURLs(pref.urls, &tabs); } else if (pref.type == SessionStartupPref::DEFAULT && !tabs.empty()) { // Make sure the home page is opened even if there are pinned tabs. std::vector<GURL> urls; AddStartupURLs(&urls); UrlsToTabs(urls, &tabs); } if (tabs.empty()) return false; Browser* browser = OpenTabsInBrowser(NULL, true, tabs); AddInfoBarsIfNecessary(browser); return true; } void BrowserInit::LaunchWithProfile::AddUniqueURLs( const std::vector<GURL>& urls, std::vector<Tab>* tabs) { size_t num_existing_tabs = tabs->size(); for (size_t i = 0; i < urls.size(); ++i) { bool in_tabs = false; for (size_t j = 0; j < num_existing_tabs; ++j) { if (urls[i] == (*tabs)[j].url) { in_tabs = true; break; } } if (!in_tabs) { BrowserInit::LaunchWithProfile::Tab tab; tab.is_pinned = false; tab.url = urls[i]; tabs->push_back(tab); } } } Browser* BrowserInit::LaunchWithProfile::OpenURLsInBrowser( Browser* browser, bool process_startup, const std::vector<GURL>& urls) { std::vector<Tab> tabs; UrlsToTabs(urls, &tabs); return OpenTabsInBrowser(browser, process_startup, tabs); } Browser* BrowserInit::LaunchWithProfile::OpenTabsInBrowser( Browser* browser, bool process_startup, const std::vector<Tab>& tabs) { DCHECK(!tabs.empty()); // If we don't yet have a profile, try to use the one we're given from // |browser|. While we may not end up actually using |browser| (since it // could be a popup window), we can at least use the profile. if (!profile_ && browser) profile_ = browser->profile(); if (!browser || browser->type() != Browser::TYPE_NORMAL) { browser = Browser::Create(profile_); } else { #if defined(TOOLKIT_GTK) // Setting the time of the last action on the window here allows us to steal // focus, which is what the user wants when opening a new tab in an existing // browser window. gtk_util::SetWMLastUserActionTime(browser->window()->GetNativeHandle()); #endif } #if !defined(OS_MACOSX) // In kiosk mode, we want to always be fullscreen, so switch to that now. if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode)) browser->ToggleFullscreenMode(); #endif bool first_tab = true; for (size_t i = 0; i < tabs.size(); ++i) { // We skip URLs that we'd have to launch an external protocol handler for. // This avoids us getting into an infinite loop asking ourselves to open // a URL, should the handler be (incorrectly) configured to be us. Anyone // asking us to open such a URL should really ask the handler directly. if (!process_startup && !net::URLRequest::IsHandledURL(tabs[i].url)) continue; int add_types = first_tab ? TabStripModel::ADD_ACTIVE : TabStripModel::ADD_NONE; add_types |= TabStripModel::ADD_FORCE_INDEX; if (tabs[i].is_pinned) add_types |= TabStripModel::ADD_PINNED; int index = browser->GetIndexForInsertionDuringRestore(i); browser::NavigateParams params(browser, tabs[i].url, PageTransition::START_PAGE); params.disposition = first_tab ? NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB; params.tabstrip_index = index; params.tabstrip_add_types = add_types; params.extension_app_id = tabs[i].app_id; browser::Navigate(¶ms); first_tab = false; } browser->window()->Show(); // TODO(jcampan): http://crbug.com/8123 we should not need to set the initial // focus explicitly. browser->GetSelectedTabContents()->view()->SetInitialFocus(); return browser; } void BrowserInit::LaunchWithProfile::AddInfoBarsIfNecessary(Browser* browser) { if (!browser || !profile_ || browser->tab_count() == 0) return; TabContents* tab_contents = browser->GetSelectedTabContents(); AddCrashedInfoBarIfNecessary(tab_contents); AddBadFlagsInfoBarIfNecessary(tab_contents); AddDNSCertProvenanceCheckingWarningInfoBarIfNecessary(tab_contents); AddObsoleteSystemInfoBarIfNecessary(tab_contents); } void BrowserInit::LaunchWithProfile::AddCrashedInfoBarIfNecessary( TabContents* tab) { // Assume that if the user is launching incognito they were previously // running incognito so that we have nothing to restore from. if (!profile_->DidLastSessionExitCleanly() && !profile_->IsOffTheRecord()) { // The last session didn't exit cleanly. Show an infobar to the user // so that they can restore if they want. The delegate deletes itself when // it is closed. tab->AddInfoBar(new SessionCrashedInfoBarDelegate(tab)); } } void BrowserInit::LaunchWithProfile::AddBadFlagsInfoBarIfNecessary( TabContents* tab) { // Unsupported flags for which to display a warning that "stability and // security will suffer". static const char* kBadFlags[] = { // These imply disabling the sandbox. switches::kSingleProcess, switches::kNoSandbox, switches::kInProcessWebGL, // These are scary features for developers that shouldn't be turned on // persistently. switches::kEnableNaCl, NULL }; const char* bad_flag = NULL; for (const char** flag = kBadFlags; *flag; ++flag) { if (command_line_.HasSwitch(*flag)) { bad_flag = *flag; break; } } if (bad_flag) { tab->AddInfoBar(new SimpleAlertInfoBarDelegate(tab, NULL, l10n_util::GetStringFUTF16(IDS_BAD_FLAGS_WARNING_MESSAGE, UTF8ToUTF16(std::string("--") + bad_flag)), false)); } } class LearnMoreInfoBar : public LinkInfoBarDelegate { public: explicit LearnMoreInfoBar(TabContents* tab_contents, const string16& message, const GURL& url); virtual ~LearnMoreInfoBar(); virtual string16 GetMessageTextWithOffset(size_t* link_offset) const OVERRIDE; virtual string16 GetLinkText() const OVERRIDE; virtual bool LinkClicked(WindowOpenDisposition disposition) OVERRIDE; private: TabContents* const tab_contents_; string16 message_; GURL learn_more_url_; DISALLOW_COPY_AND_ASSIGN(LearnMoreInfoBar); }; LearnMoreInfoBar::LearnMoreInfoBar(TabContents* tab_contents, const string16& message, const GURL& url) : LinkInfoBarDelegate(tab_contents), tab_contents_(tab_contents), message_(message), learn_more_url_(url) { } LearnMoreInfoBar::~LearnMoreInfoBar() { } string16 LearnMoreInfoBar::GetMessageTextWithOffset(size_t* link_offset) const { string16 text = message_; text.push_back(' '); // Add a space before the following link. *link_offset = text.size(); return text; } string16 LearnMoreInfoBar::GetLinkText() const { return l10n_util::GetStringUTF16(IDS_LEARN_MORE); } bool LearnMoreInfoBar::LinkClicked(WindowOpenDisposition disposition) { tab_contents_->OpenURL(learn_more_url_, GURL(), disposition, PageTransition::LINK); return false; } // This is the page which provides information on DNS certificate provenance // checking. void BrowserInit::LaunchWithProfile:: AddDNSCertProvenanceCheckingWarningInfoBarIfNecessary(TabContents* tab) { if (!command_line_.HasSwitch(switches::kEnableDNSCertProvenanceChecking)) return; const char* kLearnMoreURL = "http://dev.chromium.org/dnscertprovenancechecking"; string16 message = l10n_util::GetStringUTF16( IDS_DNS_CERT_PROVENANCE_CHECKING_WARNING_MESSAGE); tab->AddInfoBar(new LearnMoreInfoBar(tab, message, GURL(kLearnMoreURL))); } void BrowserInit::LaunchWithProfile::AddObsoleteSystemInfoBarIfNecessary( TabContents* tab) { #if defined(TOOLKIT_USES_GTK) // We've deprecated support for Ubuntu Hardy. Rather than attempting to // determine whether you're using that, we instead key off the GTK version; // this will also deprecate other distributions (including variants of Ubuntu) // that are of a similar age. // Version key: // Ubuntu Hardy: GTK 2.12 // RHEL 6: GTK 2.18 // Ubuntu Lucid: GTK 2.20 if (gtk_check_version(2, 18, 0)) { string16 message = l10n_util::GetStringFUTF16(IDS_SYSTEM_OBSOLETE_MESSAGE, l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)); // Link to an article in the help center on minimum system requirements. const char* kLearnMoreURL = "http://www.google.com/support/chrome/bin/answer.py?answer=95411"; tab->AddInfoBar(new LearnMoreInfoBar(tab, message, GURL(kLearnMoreURL))); } #endif } void BrowserInit::LaunchWithProfile::AddStartupURLs( std::vector<GURL>* startup_urls) const { // If we have urls specified beforehand (i.e. from command line) use them // and nothing else. if (!startup_urls->empty()) return; // If we have urls specified by the first run master preferences use them // and nothing else. if (browser_init_) { if (!browser_init_->first_run_tabs_.empty()) { std::vector<GURL>::iterator it = browser_init_->first_run_tabs_.begin(); while (it != browser_init_->first_run_tabs_.end()) { // Replace magic names for the actual urls. if (it->host() == "new_tab_page") { startup_urls->push_back(GURL(chrome::kChromeUINewTabURL)); } else if (it->host() == "welcome_page") { startup_urls->push_back(GetWelcomePageURL()); } else { startup_urls->push_back(*it); } ++it; } browser_init_->first_run_tabs_.clear(); return; } } // Otherwise open at least the new tab page (and the welcome page, if this // is the first time the browser is being started), or the set of URLs // specified on the command line. startup_urls->push_back(GURL()); // New tab page. PrefService* prefs = g_browser_process->local_state(); if (prefs->FindPreference(prefs::kShouldShowWelcomePage) && prefs->GetBoolean(prefs::kShouldShowWelcomePage)) { // Reset the preference so we don't show the welcome page next time. prefs->ClearPref(prefs::kShouldShowWelcomePage); startup_urls->push_back(GetWelcomePageURL()); } } void BrowserInit::LaunchWithProfile::CheckDefaultBrowser(Profile* profile) { // We do not check if we are the default browser if: // - the user said "don't ask me again" on the infobar earlier. // - this is the first launch after the first run flow. // - There is a policy in control of this setting. if (!profile->GetPrefs()->GetBoolean(prefs::kCheckDefaultBrowser) || FirstRun::IsChromeFirstRun()) { return; } if (g_browser_process->local_state()->IsManagedPreference( prefs::kDefaultBrowserSettingEnabled)) { if (g_browser_process->local_state()->GetBoolean( prefs::kDefaultBrowserSettingEnabled)) { BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, NewRunnableFunction( &ShellIntegration::SetAsDefaultBrowser)); } else { // TODO(pastarmovj): We can't really do anything meaningful here yet but // just prevent showing the infobar. } return; } BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, new CheckDefaultBrowserTask()); } std::vector<GURL> BrowserInit::GetURLsFromCommandLine( const CommandLine& command_line, const FilePath& cur_dir, Profile* profile) { std::vector<GURL> urls; const std::vector<CommandLine::StringType>& params = command_line.args(); for (size_t i = 0; i < params.size(); ++i) { FilePath param = FilePath(params[i]); // Handle Vista way of searching - "? <search-term>" if (param.value().size() > 2 && param.value()[0] == '?' && param.value()[1] == ' ') { const TemplateURL* default_provider = profile->GetTemplateURLModel()->GetDefaultSearchProvider(); if (default_provider && default_provider->url()) { const TemplateURLRef* search_url = default_provider->url(); DCHECK(search_url->SupportsReplacement()); string16 search_term = param.LossyDisplayName().substr(2); urls.push_back(GURL(search_url->ReplaceSearchTerms( *default_provider, search_term, TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, string16()))); continue; } } // Otherwise, fall through to treating it as a URL. // This will create a file URL or a regular URL. // This call can (in rare circumstances) block the UI thread. // Allow it until this bug is fixed. // http://code.google.com/p/chromium/issues/detail?id=60641 GURL url; { base::ThreadRestrictions::ScopedAllowIO allow_io; url = URLFixerUpper::FixupRelativeFile(cur_dir, param); } // Exclude dangerous schemes. if (url.is_valid()) { ChildProcessSecurityPolicy *policy = ChildProcessSecurityPolicy::GetInstance(); if (policy->IsWebSafeScheme(url.scheme()) || url.SchemeIs(chrome::kFileScheme) || #if defined(OS_CHROMEOS) // In ChromeOS, allow a settings page to be specified on the // command line. See ExistingUserController::OnLoginSuccess. (url.spec().find(chrome::kChromeUISettingsURL) == 0) || #endif (url.spec().compare(chrome::kAboutBlankURL) == 0)) { urls.push_back(url); } } } return urls; } bool BrowserInit::ProcessCmdLineImpl(const CommandLine& command_line, const FilePath& cur_dir, bool process_startup, Profile* profile, int* return_code, BrowserInit* browser_init) { DCHECK(profile); if (process_startup) { if (command_line.HasSwitch(switches::kDisablePromptOnRepost)) NavigationController::DisablePromptOnRepost(); // Look for the testing channel ID ONLY during process startup if (command_line.HasSwitch(switches::kTestingChannelID)) { std::string testing_channel_id = command_line.GetSwitchValueASCII( switches::kTestingChannelID); // TODO(sanjeevr) Check if we need to make this a singleton for // compatibility with the old testing code // If there are any extra parameters, we expect each one to generate a // new tab; if there are none then we get one homepage tab. int expected_tab_count = 1; if (command_line.HasSwitch(switches::kNoStartupWindow)) { expected_tab_count = 0; #if defined(OS_CHROMEOS) // kLoginManager will cause Chrome to start up with the ChromeOS login // screen instead of a browser window, so it won't load any tabs. } else if (command_line.HasSwitch(switches::kLoginManager)) { expected_tab_count = 0; #endif } else if (command_line.HasSwitch(switches::kRestoreLastSession)) { std::string restore_session_value( command_line.GetSwitchValueASCII(switches::kRestoreLastSession)); base::StringToInt(restore_session_value, &expected_tab_count); } else { std::vector<GURL> urls_to_open = GetURLsFromCommandLine( command_line, cur_dir, profile); expected_tab_count = std::max(1, static_cast<int>(urls_to_open.size())); } if (!CreateAutomationProvider<TestingAutomationProvider>( testing_channel_id, profile, static_cast<size_t>(expected_tab_count))) return false; } } bool silent_launch = false; if (command_line.HasSwitch(switches::kAutomationClientChannelID)) { std::string automation_channel_id = command_line.GetSwitchValueASCII( switches::kAutomationClientChannelID); // If there are any extra parameters, we expect each one to generate a // new tab; if there are none then we have no tabs std::vector<GURL> urls_to_open = GetURLsFromCommandLine( command_line, cur_dir, profile); size_t expected_tabs = std::max(static_cast<int>(urls_to_open.size()), 0); if (expected_tabs == 0) silent_launch = true; if (command_line.HasSwitch(switches::kChromeFrame)) { if (!CreateAutomationProvider<ChromeFrameAutomationProvider>( automation_channel_id, profile, expected_tabs)) return false; } else { if (!CreateAutomationProvider<AutomationProvider>( automation_channel_id, profile, expected_tabs)) return false; } } // If we have been invoked to display a desktop notification on behalf of // the service process, we do not want to open any browser windows. if (command_line.HasSwitch(switches::kNotifyCloudPrintTokenExpired)) { silent_launch = true; profile->GetCloudPrintProxyService()->ShowTokenExpiredNotification(); } // If we are just displaying a print dialog we shouldn't open browser // windows. if (print_dialog_cloud::CreatePrintDialogFromCommandLine(command_line)) { silent_launch = true; } if (command_line.HasSwitch(switches::kExplicitlyAllowedPorts)) { std::string allowed_ports = command_line.GetSwitchValueASCII(switches::kExplicitlyAllowedPorts); net::SetExplicitlyAllowedPorts(allowed_ports); } #if defined(OS_CHROMEOS) // The browser will be launched after the user logs in. if (command_line.HasSwitch(switches::kLoginManager) || command_line.HasSwitch(switches::kLoginPassword)) { silent_launch = true; } #endif #if defined(HAVE_XINPUT2) && defined(TOUCH_UI) // Get a list of pointer-devices that should be treated as touch-devices. // TODO(sad): Instead of/in addition to getting the list from the // command-line, query X for a list of touch devices. std::string touch_devices = command_line.GetSwitchValueASCII(switches::kTouchDevices); if (!touch_devices.empty()) { std::vector<std::string> devs; std::vector<unsigned int> device_ids; unsigned int devid; base::SplitString(touch_devices, ',', &devs); for (std::vector<std::string>::iterator iter = devs.begin(); iter != devs.end(); ++iter) { if (base::StringToInt(*iter, reinterpret_cast<int*>(&devid))) { device_ids.push_back(devid); } else { DLOG(WARNING) << "Invalid touch-device id: " << *iter; } } views::SetTouchDeviceList(device_ids); } #endif // If we don't want to launch a new browser window or tab (in the case // of an automation request), we are done here. if (!silent_launch) { return browser_init->LaunchBrowser( command_line, profile, cur_dir, process_startup, return_code); } return true; } template <class AutomationProviderClass> bool BrowserInit::CreateAutomationProvider(const std::string& channel_id, Profile* profile, size_t expected_tabs) { scoped_refptr<AutomationProviderClass> automation = new AutomationProviderClass(profile); if (!automation->InitializeChannel(channel_id)) return false; automation->SetExpectedTabCount(expected_tabs); AutomationProviderList* list = g_browser_process->InitAutomationProviderList(); DCHECK(list); list->AddProvider(automation); return true; }