| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/ui/browser_init.h" | |
| 6 | |
| 7 #include <algorithm> // For max(). | |
| 8 #include <set> | |
| 9 | |
| 10 #include "base/bind.h" | |
| 11 #include "base/bind_helpers.h" | |
| 12 #include "base/compiler_specific.h" | |
| 13 #include "base/environment.h" | |
| 14 #include "base/event_recorder.h" | |
| 15 #include "base/file_path.h" | |
| 16 #include "base/lazy_instance.h" | |
| 17 #include "base/memory/scoped_ptr.h" | |
| 18 #include "base/metrics/histogram.h" | |
| 19 #include "base/path_service.h" | |
| 20 #include "base/string_number_conversions.h" | |
| 21 #include "base/string_split.h" | |
| 22 #include "base/threading/thread_restrictions.h" | |
| 23 #include "base/utf_string_conversions.h" | |
| 24 #include "chrome/browser/auto_launch_trial.h" | |
| 25 #include "chrome/browser/automation/automation_provider.h" | |
| 26 #include "chrome/browser/automation/automation_provider_list.h" | |
| 27 #include "chrome/browser/automation/chrome_frame_automation_provider.h" | |
| 28 #include "chrome/browser/automation/testing_automation_provider.h" | |
| 29 #include "chrome/browser/browser_process.h" | |
| 30 #include "chrome/browser/component_updater/component_updater_service.h" | |
| 31 #include "chrome/browser/component_updater/flash_component_installer.h" | |
| 32 #include "chrome/browser/component_updater/pnacl/pnacl_component_installer.h" | |
| 33 #include "chrome/browser/component_updater/recovery_component_installer.h" | |
| 34 #include "chrome/browser/component_updater/swiftshader_component_installer.h" | |
| 35 #include "chrome/browser/custom_handlers/protocol_handler_registry.h" | |
| 36 #include "chrome/browser/defaults.h" | |
| 37 #include "chrome/browser/extensions/extension_creator.h" | |
| 38 #include "chrome/browser/extensions/extension_service.h" | |
| 39 #include "chrome/browser/extensions/pack_extension_job.h" | |
| 40 #include "chrome/browser/first_run/first_run.h" | |
| 41 #include "chrome/browser/google/google_util.h" | |
| 42 #include "chrome/browser/infobars/infobar_tab_helper.h" | |
| 43 #include "chrome/browser/net/crl_set_fetcher.h" | |
| 44 #include "chrome/browser/net/predictor.h" | |
| 45 #include "chrome/browser/net/url_fixer_upper.h" | |
| 46 #include "chrome/browser/notifications/desktop_notification_service.h" | |
| 47 #include "chrome/browser/prefs/incognito_mode_prefs.h" | |
| 48 #include "chrome/browser/prefs/pref_service.h" | |
| 49 #include "chrome/browser/prefs/session_startup_pref.h" | |
| 50 #include "chrome/browser/printing/cloud_print/cloud_print_proxy_service.h" | |
| 51 #include "chrome/browser/printing/cloud_print/cloud_print_proxy_service_factory.
h" | |
| 52 #include "chrome/browser/printing/print_dialog_cloud.h" | |
| 53 #include "chrome/browser/profiles/profile.h" | |
| 54 #include "chrome/browser/profiles/profile_io_data.h" | |
| 55 #include "chrome/browser/profiles/profile_manager.h" | |
| 56 #include "chrome/browser/protector/base_setting_change.h" | |
| 57 #include "chrome/browser/protector/protected_prefs_watcher.h" | |
| 58 #include "chrome/browser/protector/protector_service.h" | |
| 59 #include "chrome/browser/protector/protector_service_factory.h" | |
| 60 #include "chrome/browser/protector/protector_utils.h" | |
| 61 #include "chrome/browser/search_engines/template_url.h" | |
| 62 #include "chrome/browser/search_engines/template_url_service.h" | |
| 63 #include "chrome/browser/search_engines/template_url_service_factory.h" | |
| 64 #include "chrome/browser/sessions/session_restore.h" | |
| 65 #include "chrome/browser/sessions/session_service.h" | |
| 66 #include "chrome/browser/sessions/session_service_factory.h" | |
| 67 #include "chrome/browser/shell_integration.h" | |
| 68 #include "chrome/browser/tab_contents/link_infobar_delegate.h" | |
| 69 #include "chrome/browser/tabs/pinned_tab_codec.h" | |
| 70 #include "chrome/browser/tabs/tab_strip_model.h" | |
| 71 #include "chrome/browser/ui/browser_list.h" | |
| 72 #include "chrome/browser/ui/browser_navigator.h" | |
| 73 #include "chrome/browser/ui/browser_window.h" | |
| 74 #include "chrome/browser/ui/startup/autolaunch_prompt.h" | |
| 75 #include "chrome/browser/ui/startup/bad_flags_prompt.h" | |
| 76 #include "chrome/browser/ui/startup/default_browser_prompt.h" | |
| 77 #include "chrome/browser/ui/startup/obsolete_os_prompt.h" | |
| 78 #include "chrome/browser/ui/startup/session_crashed_prompt.h" | |
| 79 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" | |
| 80 #include "chrome/browser/ui/webui/ntp/app_launcher_handler.h" | |
| 81 #include "chrome/browser/ui/webui/sync_promo/sync_promo_trial.h" | |
| 82 #include "chrome/browser/ui/webui/sync_promo/sync_promo_ui.h" | |
| 83 #include "chrome/common/chrome_constants.h" | |
| 84 #include "chrome/common/chrome_notification_types.h" | |
| 85 #include "chrome/common/chrome_paths.h" | |
| 86 #include "chrome/common/chrome_result_codes.h" | |
| 87 #include "chrome/common/chrome_switches.h" | |
| 88 #include "chrome/common/chrome_version_info.h" | |
| 89 #include "chrome/common/extensions/extension_constants.h" | |
| 90 #include "chrome/common/pref_names.h" | |
| 91 #include "chrome/common/url_constants.h" | |
| 92 #include "chrome/installer/util/browser_distribution.h" | |
| 93 #include "content/public/browser/browser_thread.h" | |
| 94 #include "content/public/browser/child_process_security_policy.h" | |
| 95 #include "content/public/browser/web_contents.h" | |
| 96 #include "content/public/browser/web_contents_view.h" | |
| 97 #include "grit/locale_settings.h" | |
| 98 #include "net/base/net_util.h" | |
| 99 #include "ui/base/l10n/l10n_util.h" | |
| 100 #include "ui/base/resource/resource_bundle.h" | |
| 101 | |
| 102 #if defined(OS_CHROMEOS) | |
| 103 #include "chrome/browser/chromeos/profile_startup.h" | |
| 104 #endif | |
| 105 | |
| 106 #if defined(OS_MACOSX) | |
| 107 #include "base/mac/mac_util.h" | |
| 108 #include "chrome/browser/ui/cocoa/keystone_infobar_delegate.h" | |
| 109 #endif | |
| 110 | |
| 111 #if defined(TOOLKIT_GTK) | |
| 112 #include "chrome/browser/ui/gtk/gtk_util.h" | |
| 113 #endif | |
| 114 | |
| 115 #if defined(TOOLKIT_VIEWS) && defined(OS_LINUX) | |
| 116 #include "ui/base/touch/touch_factory.h" | |
| 117 #endif | |
| 118 | |
| 119 #if defined(OS_WIN) | |
| 120 #include "base/win/windows_version.h" | |
| 121 #include "chrome/browser/ui/browser_init_win.h" | |
| 122 #endif | |
| 123 | |
| 124 using content::BrowserThread; | |
| 125 using content::ChildProcessSecurityPolicy; | |
| 126 using content::WebContents; | |
| 127 using protector::BaseSettingChange; | |
| 128 using protector::ProtectedPrefsWatcher; | |
| 129 using protector::ProtectorService; | |
| 130 using protector::ProtectorServiceFactory; | |
| 131 | |
| 132 namespace { | |
| 133 | |
| 134 bool in_synchronous_profile_launch = false; | |
| 135 | |
| 136 // Utility functions ---------------------------------------------------------- | |
| 137 | |
| 138 enum LaunchMode { | |
| 139 LM_TO_BE_DECIDED = 0, // Possibly direct launch or via a shortcut. | |
| 140 LM_AS_WEBAPP, // Launched as a installed web application. | |
| 141 LM_WITH_URLS, // Launched with urls in the cmd line. | |
| 142 LM_SHORTCUT_NONE, // Not launched from a shortcut. | |
| 143 LM_SHORTCUT_NONAME, // Launched from shortcut but no name available. | |
| 144 LM_SHORTCUT_UNKNOWN, // Launched from user-defined shortcut. | |
| 145 LM_SHORTCUT_QUICKLAUNCH, // Launched from the quick launch bar. | |
| 146 LM_SHORTCUT_DESKTOP, // Launched from a desktop shortcut. | |
| 147 LM_SHORTCUT_TASKBAR, // Launched from the taskbar. | |
| 148 LM_LINUX_MAC_BEOS // Other OS buckets start here. | |
| 149 }; | |
| 150 | |
| 151 #if defined(OS_WIN) | |
| 152 // Undocumented flag in the startup info structure tells us what shortcut was | |
| 153 // used to launch the browser. See http://www.catch22.net/tuts/undoc01 for | |
| 154 // more information. Confirmed to work on XP, Vista and Win7. | |
| 155 LaunchMode GetLaunchShortcutKind() { | |
| 156 STARTUPINFOW si = { sizeof(si) }; | |
| 157 GetStartupInfoW(&si); | |
| 158 if (si.dwFlags & 0x800) { | |
| 159 if (!si.lpTitle) | |
| 160 return LM_SHORTCUT_NONAME; | |
| 161 string16 shortcut(si.lpTitle); | |
| 162 // The windows quick launch path is not localized. | |
| 163 if (shortcut.find(L"\\Quick Launch\\") != string16::npos) { | |
| 164 if (base::win::GetVersion() >= base::win::VERSION_WIN7) | |
| 165 return LM_SHORTCUT_TASKBAR; | |
| 166 else | |
| 167 return LM_SHORTCUT_QUICKLAUNCH; | |
| 168 } | |
| 169 scoped_ptr<base::Environment> env(base::Environment::Create()); | |
| 170 std::string appdata_path; | |
| 171 env->GetVar("USERPROFILE", &appdata_path); | |
| 172 if (!appdata_path.empty() && | |
| 173 shortcut.find(ASCIIToWide(appdata_path)) != std::wstring::npos) | |
| 174 return LM_SHORTCUT_DESKTOP; | |
| 175 return LM_SHORTCUT_UNKNOWN; | |
| 176 } | |
| 177 return LM_SHORTCUT_NONE; | |
| 178 } | |
| 179 #else | |
| 180 // TODO(cpu): Port to other platforms. | |
| 181 LaunchMode GetLaunchShortcutKind() { | |
| 182 return LM_LINUX_MAC_BEOS; | |
| 183 } | |
| 184 #endif | |
| 185 | |
| 186 // Log in a histogram the frequency of launching by the different methods. See | |
| 187 // LaunchMode enum for the actual values of the buckets. | |
| 188 void RecordLaunchModeHistogram(LaunchMode mode) { | |
| 189 int bucket = (mode == LM_TO_BE_DECIDED) ? GetLaunchShortcutKind() : mode; | |
| 190 UMA_HISTOGRAM_COUNTS_100("Launch.Modes", bucket); | |
| 191 } | |
| 192 | |
| 193 GURL GetWelcomePageURL() { | |
| 194 std::string welcome_url = l10n_util::GetStringUTF8(IDS_WELCOME_PAGE_URL); | |
| 195 return GURL(welcome_url); | |
| 196 } | |
| 197 | |
| 198 void UrlsToTabs(const std::vector<GURL>& urls, | |
| 199 std::vector<BrowserInit::LaunchWithProfile::Tab>* tabs) { | |
| 200 for (size_t i = 0; i < urls.size(); ++i) { | |
| 201 BrowserInit::LaunchWithProfile::Tab tab; | |
| 202 tab.is_pinned = false; | |
| 203 tab.url = urls[i]; | |
| 204 tabs->push_back(tab); | |
| 205 } | |
| 206 } | |
| 207 | |
| 208 // Return true if the command line option --app-id is used. Set | |
| 209 // |out_extension| to the app to open, and |out_launch_container| | |
| 210 // to the type of window into which the app should be open. | |
| 211 bool GetAppLaunchContainer( | |
| 212 Profile* profile, | |
| 213 const std::string& app_id, | |
| 214 const Extension** out_extension, | |
| 215 extension_misc::LaunchContainer* out_launch_container) { | |
| 216 | |
| 217 ExtensionService* extensions_service = profile->GetExtensionService(); | |
| 218 const Extension* extension = | |
| 219 extensions_service->GetExtensionById(app_id, false); | |
| 220 | |
| 221 // The extension with id |app_id| may have been uninstalled. | |
| 222 if (!extension) | |
| 223 return false; | |
| 224 | |
| 225 // Look at preferences to find the right launch container. If no | |
| 226 // preference is set, launch as a window. | |
| 227 extension_misc::LaunchContainer launch_container = | |
| 228 extensions_service->extension_prefs()->GetLaunchContainer( | |
| 229 extension, ExtensionPrefs::LAUNCH_WINDOW); | |
| 230 | |
| 231 *out_extension = extension; | |
| 232 *out_launch_container = launch_container; | |
| 233 return true; | |
| 234 } | |
| 235 | |
| 236 void RecordCmdLineAppHistogram() { | |
| 237 AppLauncherHandler::RecordAppLaunchType( | |
| 238 extension_misc::APP_LAUNCH_CMD_LINE_APP); | |
| 239 } | |
| 240 | |
| 241 void RecordAppLaunches( | |
| 242 Profile* profile, | |
| 243 const std::vector<GURL>& cmd_line_urls, | |
| 244 const std::vector<BrowserInit::LaunchWithProfile::Tab>& autolaunch_tabs) { | |
| 245 ExtensionService* extension_service = profile->GetExtensionService(); | |
| 246 DCHECK(extension_service); | |
| 247 for (size_t i = 0; i < cmd_line_urls.size(); ++i) { | |
| 248 if (extension_service->IsInstalledApp(cmd_line_urls.at(i))) { | |
| 249 AppLauncherHandler::RecordAppLaunchType( | |
| 250 extension_misc::APP_LAUNCH_CMD_LINE_URL); | |
| 251 } | |
| 252 } | |
| 253 for (size_t i = 0; i < autolaunch_tabs.size(); ++i) { | |
| 254 if (extension_service->IsInstalledApp(autolaunch_tabs.at(i).url)) { | |
| 255 AppLauncherHandler::RecordAppLaunchType( | |
| 256 extension_misc::APP_LAUNCH_AUTOLAUNCH); | |
| 257 } | |
| 258 } | |
| 259 } | |
| 260 | |
| 261 void RegisterComponentsForUpdate(const CommandLine& command_line) { | |
| 262 ComponentUpdateService* cus = g_browser_process->component_updater(); | |
| 263 if (!cus) | |
| 264 return; | |
| 265 // Registration can be before of after cus->Start() so it is ok to post | |
| 266 // a task to the UI thread to do registration once you done the necessary | |
| 267 // file IO to know you existing component version. | |
| 268 RegisterRecoveryComponent(cus, g_browser_process->local_state()); | |
| 269 RegisterPepperFlashComponent(cus); | |
| 270 RegisterNPAPIFlashComponent(cus); | |
| 271 RegisterSwiftShaderComponent(cus); | |
| 272 | |
| 273 // CRLSetFetcher attempts to load a CRL set from either the local disk or | |
| 274 // network. | |
| 275 if (!command_line.HasSwitch(switches::kDisableCRLSets)) | |
| 276 g_browser_process->crl_set_fetcher()->StartInitialLoad(cus); | |
| 277 | |
| 278 // This developer version of Pnacl should only be installed for developers. | |
| 279 if (command_line.HasSwitch(switches::kEnablePnacl)) { | |
| 280 RegisterPnaclComponent(cus); | |
| 281 } | |
| 282 | |
| 283 cus->Start(); | |
| 284 } | |
| 285 | |
| 286 // Keeps track on which profiles have been launched. | |
| 287 class ProfileLaunchObserver : public content::NotificationObserver { | |
| 288 public: | |
| 289 ProfileLaunchObserver() { | |
| 290 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED, | |
| 291 content::NotificationService::AllSources()); | |
| 292 } | |
| 293 virtual ~ProfileLaunchObserver() {} | |
| 294 | |
| 295 virtual void Observe(int type, | |
| 296 const content::NotificationSource& source, | |
| 297 const content::NotificationDetails& details) OVERRIDE { | |
| 298 switch (type) { | |
| 299 case chrome::NOTIFICATION_PROFILE_DESTROYED: { | |
| 300 Profile* profile = content::Source<Profile>(source).ptr(); | |
| 301 launched_profiles.erase(profile); | |
| 302 break; | |
| 303 } | |
| 304 default: | |
| 305 NOTREACHED(); | |
| 306 } | |
| 307 } | |
| 308 | |
| 309 bool HasBeenLaunched(const Profile* profile) { | |
| 310 return launched_profiles.find(profile) != launched_profiles.end(); | |
| 311 } | |
| 312 | |
| 313 void AddLaunched(const Profile* profile) { | |
| 314 launched_profiles.insert(profile); | |
| 315 } | |
| 316 | |
| 317 private: | |
| 318 std::set<const Profile*> launched_profiles; | |
| 319 content::NotificationRegistrar registrar_; | |
| 320 | |
| 321 DISALLOW_COPY_AND_ASSIGN(ProfileLaunchObserver); | |
| 322 }; | |
| 323 | |
| 324 base::LazyInstance<ProfileLaunchObserver> profile_launch_observer = | |
| 325 LAZY_INSTANCE_INITIALIZER; | |
| 326 | |
| 327 // Returns true if |profile| has exited uncleanly and has not been launched | |
| 328 // after the unclean exit. | |
| 329 bool HasPendingUncleanExit(Profile* profile) { | |
| 330 return !profile->DidLastSessionExitCleanly() && | |
| 331 !profile_launch_observer.Get().HasBeenLaunched(profile); | |
| 332 } | |
| 333 | |
| 334 } // namespace | |
| 335 | |
| 336 | |
| 337 // BrowserInit ---------------------------------------------------------------- | |
| 338 | |
| 339 BrowserInit::BrowserInit() {} | |
| 340 | |
| 341 BrowserInit::~BrowserInit() {} | |
| 342 | |
| 343 // static | |
| 344 bool BrowserInit::was_restarted_read_ = false; | |
| 345 | |
| 346 void BrowserInit::AddFirstRunTab(const GURL& url) { | |
| 347 first_run_tabs_.push_back(url); | |
| 348 } | |
| 349 | |
| 350 // static | |
| 351 bool BrowserInit::InSynchronousProfileLaunch() { | |
| 352 return in_synchronous_profile_launch; | |
| 353 } | |
| 354 | |
| 355 bool BrowserInit::LaunchBrowser(const CommandLine& command_line, | |
| 356 Profile* profile, | |
| 357 const FilePath& cur_dir, | |
| 358 IsProcessStartup process_startup, | |
| 359 IsFirstRun is_first_run, | |
| 360 int* return_code) { | |
| 361 in_synchronous_profile_launch = process_startup == IS_PROCESS_STARTUP; | |
| 362 DCHECK(profile); | |
| 363 | |
| 364 // Continue with the incognito profile from here on if Incognito mode | |
| 365 // is forced. | |
| 366 if (IncognitoModePrefs::ShouldLaunchIncognito(command_line, | |
| 367 profile->GetPrefs())) { | |
| 368 profile = profile->GetOffTheRecordProfile(); | |
| 369 } else if (command_line.HasSwitch(switches::kIncognito)) { | |
| 370 LOG(WARNING) << "Incognito mode disabled by policy, launching a normal " | |
| 371 << "browser session."; | |
| 372 } | |
| 373 | |
| 374 BrowserInit::LaunchWithProfile lwp(cur_dir, command_line, this, is_first_run); | |
| 375 std::vector<GURL> urls_to_launch = BrowserInit::GetURLsFromCommandLine( | |
| 376 command_line, cur_dir, profile); | |
| 377 bool launched = lwp.Launch(profile, urls_to_launch, | |
| 378 in_synchronous_profile_launch); | |
| 379 in_synchronous_profile_launch = false; | |
| 380 | |
| 381 if (!launched) { | |
| 382 LOG(ERROR) << "launch error"; | |
| 383 if (return_code) | |
| 384 *return_code = chrome::RESULT_CODE_INVALID_CMDLINE_URL; | |
| 385 return false; | |
| 386 } | |
| 387 profile_launch_observer.Get().AddLaunched(profile); | |
| 388 | |
| 389 #if defined(OS_CHROMEOS) | |
| 390 chromeos::ProfileStartup(profile, process_startup); | |
| 391 #endif | |
| 392 return true; | |
| 393 } | |
| 394 | |
| 395 // static | |
| 396 bool BrowserInit::WasRestarted() { | |
| 397 // Stores the value of the preference kWasRestarted had when it was read. | |
| 398 static bool was_restarted = false; | |
| 399 | |
| 400 if (!was_restarted_read_) { | |
| 401 PrefService* pref_service = g_browser_process->local_state(); | |
| 402 was_restarted = pref_service->GetBoolean(prefs::kWasRestarted); | |
| 403 pref_service->SetBoolean(prefs::kWasRestarted, false); | |
| 404 was_restarted_read_ = true; | |
| 405 } | |
| 406 return was_restarted; | |
| 407 } | |
| 408 | |
| 409 // static | |
| 410 SessionStartupPref BrowserInit::GetSessionStartupPref( | |
| 411 const CommandLine& command_line, | |
| 412 Profile* profile) { | |
| 413 SessionStartupPref pref = SessionStartupPref::GetStartupPref(profile); | |
| 414 | |
| 415 // Session restore should be avoided on the first run. | |
| 416 if (first_run::IsChromeFirstRun()) | |
| 417 pref.type = SessionStartupPref::DEFAULT; | |
| 418 | |
| 419 if (command_line.HasSwitch(switches::kRestoreLastSession) || | |
| 420 BrowserInit::WasRestarted()) { | |
| 421 pref.type = SessionStartupPref::LAST; | |
| 422 } | |
| 423 if (pref.type == SessionStartupPref::LAST && | |
| 424 IncognitoModePrefs::ShouldLaunchIncognito(command_line, | |
| 425 profile->GetPrefs())) { | |
| 426 // We don't store session information when incognito. If the user has | |
| 427 // chosen to restore last session and launched incognito, fallback to | |
| 428 // default launch behavior. | |
| 429 pref.type = SessionStartupPref::DEFAULT; | |
| 430 } | |
| 431 return pref; | |
| 432 } | |
| 433 | |
| 434 | |
| 435 // BrowserInit::LaunchWithProfile::Tab ---------------------------------------- | |
| 436 | |
| 437 BrowserInit::LaunchWithProfile::Tab::Tab() : is_app(false), is_pinned(true) {} | |
| 438 | |
| 439 BrowserInit::LaunchWithProfile::Tab::~Tab() {} | |
| 440 | |
| 441 | |
| 442 // BrowserInit::LaunchWithProfile --------------------------------------------- | |
| 443 | |
| 444 BrowserInit::LaunchWithProfile::LaunchWithProfile( | |
| 445 const FilePath& cur_dir, | |
| 446 const CommandLine& command_line, | |
| 447 IsFirstRun is_first_run) | |
| 448 : cur_dir_(cur_dir), | |
| 449 command_line_(command_line), | |
| 450 profile_(NULL), | |
| 451 browser_init_(NULL), | |
| 452 is_first_run_(is_first_run == IS_FIRST_RUN) { | |
| 453 } | |
| 454 | |
| 455 BrowserInit::LaunchWithProfile::LaunchWithProfile( | |
| 456 const FilePath& cur_dir, | |
| 457 const CommandLine& command_line, | |
| 458 BrowserInit* browser_init, | |
| 459 IsFirstRun is_first_run) | |
| 460 : cur_dir_(cur_dir), | |
| 461 command_line_(command_line), | |
| 462 profile_(NULL), | |
| 463 browser_init_(browser_init), | |
| 464 is_first_run_(is_first_run == IS_FIRST_RUN) { | |
| 465 } | |
| 466 | |
| 467 BrowserInit::LaunchWithProfile::~LaunchWithProfile() { | |
| 468 } | |
| 469 | |
| 470 bool BrowserInit::LaunchWithProfile::Launch( | |
| 471 Profile* profile, | |
| 472 const std::vector<GURL>& urls_to_open, | |
| 473 bool process_startup) { | |
| 474 DCHECK(profile); | |
| 475 profile_ = profile; | |
| 476 | |
| 477 if (command_line_.HasSwitch(switches::kDnsLogDetails)) | |
| 478 chrome_browser_net::EnablePredictorDetailedLog(true); | |
| 479 if (command_line_.HasSwitch(switches::kDnsPrefetchDisable) && | |
| 480 profile->GetNetworkPredictor()) { | |
| 481 profile->GetNetworkPredictor()->EnablePredictor(false); | |
| 482 } | |
| 483 | |
| 484 if (command_line_.HasSwitch(switches::kDumpHistogramsOnExit)) | |
| 485 base::StatisticsRecorder::set_dump_on_exit(true); | |
| 486 | |
| 487 if (command_line_.HasSwitch(switches::kRemoteDebuggingPort)) { | |
| 488 std::string port_str = | |
| 489 command_line_.GetSwitchValueASCII(switches::kRemoteDebuggingPort); | |
| 490 int64 port; | |
| 491 if (base::StringToInt64(port_str, &port) && port > 0 && port < 65535) { | |
| 492 std::string frontend_str; | |
| 493 if (command_line_.HasSwitch(switches::kRemoteDebuggingFrontend)) { | |
| 494 frontend_str = command_line_.GetSwitchValueASCII( | |
| 495 switches::kRemoteDebuggingFrontend); | |
| 496 } | |
| 497 g_browser_process->InitDevToolsHttpProtocolHandler( | |
| 498 profile, | |
| 499 "127.0.0.1", | |
| 500 static_cast<int>(port), | |
| 501 frontend_str); | |
| 502 } else { | |
| 503 DLOG(WARNING) << "Invalid http debugger port number " << port; | |
| 504 } | |
| 505 } | |
| 506 | |
| 507 // Open the required browser windows and tabs. First, see if | |
| 508 // we're being run as an application window. If so, the user | |
| 509 // opened an app shortcut. Don't restore tabs or open initial | |
| 510 // URLs in that case. The user should see the window as an app, | |
| 511 // not as chrome. | |
| 512 // Special case is when app switches are passed but we do want to restore | |
| 513 // session. In that case open app window + focus it after session is restored. | |
| 514 if (OpenApplicationWindow(profile) && !browser_defaults::kAppRestoreSession) { | |
| 515 RecordLaunchModeHistogram(LM_AS_WEBAPP); | |
| 516 } else { | |
| 517 Browser* browser_to_focus = NULL; | |
| 518 // In case of app mode + session restore we want to focus that app. | |
| 519 if (browser_defaults::kAppRestoreSession) | |
| 520 browser_to_focus = BrowserList::GetLastActive(); | |
| 521 | |
| 522 RecordLaunchModeHistogram(urls_to_open.empty()? | |
| 523 LM_TO_BE_DECIDED : LM_WITH_URLS); | |
| 524 | |
| 525 // Notify user if the Preferences backup is invalid or changes to settings | |
| 526 // affecting browser startup have been detected. | |
| 527 CheckPreferencesBackup(profile); | |
| 528 | |
| 529 ProcessLaunchURLs(process_startup, urls_to_open); | |
| 530 | |
| 531 // If this is an app launch, but we didn't open an app window, it may | |
| 532 // be an app tab. | |
| 533 OpenApplicationTab(profile); | |
| 534 | |
| 535 if (browser_to_focus) | |
| 536 browser_to_focus->GetSelectedWebContents()->GetView()->SetInitialFocus(); | |
| 537 | |
| 538 if (process_startup) { | |
| 539 if (browser_defaults::kOSSupportsOtherBrowsers && | |
| 540 !command_line_.HasSwitch(switches::kNoDefaultBrowserCheck)) { | |
| 541 if (!browser::ShowAutolaunchPrompt(profile)) | |
| 542 browser::ShowDefaultBrowserPrompt(profile); | |
| 543 } | |
| 544 #if defined(OS_MACOSX) | |
| 545 // Check whether the auto-update system needs to be promoted from user | |
| 546 // to system. | |
| 547 KeystoneInfoBar::PromotionInfoBar(profile); | |
| 548 #endif | |
| 549 } | |
| 550 } | |
| 551 | |
| 552 #if defined(OS_WIN) | |
| 553 // Print the selected page if the command line switch exists. Note that the | |
| 554 // current selected tab would be the page which will be printed. | |
| 555 if (command_line_.HasSwitch(switches::kPrint)) { | |
| 556 Browser* browser = BrowserList::GetLastActive(); | |
| 557 browser->Print(); | |
| 558 } | |
| 559 #endif | |
| 560 | |
| 561 // If we're recording or playing back, startup the EventRecorder now | |
| 562 // unless otherwise specified. | |
| 563 if (!command_line_.HasSwitch(switches::kNoEvents)) { | |
| 564 FilePath script_path; | |
| 565 PathService::Get(chrome::FILE_RECORDED_SCRIPT, &script_path); | |
| 566 | |
| 567 bool record_mode = command_line_.HasSwitch(switches::kRecordMode); | |
| 568 bool playback_mode = command_line_.HasSwitch(switches::kPlaybackMode); | |
| 569 | |
| 570 if (record_mode && chrome::kRecordModeEnabled) | |
| 571 base::EventRecorder::current()->StartRecording(script_path); | |
| 572 if (playback_mode) | |
| 573 base::EventRecorder::current()->StartPlayback(script_path); | |
| 574 } | |
| 575 | |
| 576 #if defined(OS_WIN) | |
| 577 if (process_startup) | |
| 578 ShellIntegration::MigrateChromiumShortcuts(); | |
| 579 #endif // defined(OS_WIN) | |
| 580 | |
| 581 return true; | |
| 582 } | |
| 583 | |
| 584 bool BrowserInit::LaunchWithProfile::IsAppLaunch(std::string* app_url, | |
| 585 std::string* app_id) { | |
| 586 if (command_line_.HasSwitch(switches::kApp)) { | |
| 587 if (app_url) | |
| 588 *app_url = command_line_.GetSwitchValueASCII(switches::kApp); | |
| 589 return true; | |
| 590 } | |
| 591 if (command_line_.HasSwitch(switches::kAppId)) { | |
| 592 if (app_id) | |
| 593 *app_id = command_line_.GetSwitchValueASCII(switches::kAppId); | |
| 594 return true; | |
| 595 } | |
| 596 return false; | |
| 597 } | |
| 598 | |
| 599 bool BrowserInit::LaunchWithProfile::OpenApplicationTab(Profile* profile) { | |
| 600 std::string app_id; | |
| 601 // App shortcuts to URLs always open in an app window. Because this | |
| 602 // function will open an app that should be in a tab, there is no need | |
| 603 // to look at the app URL. OpenApplicationWindow() will open app url | |
| 604 // shortcuts. | |
| 605 if (!IsAppLaunch(NULL, &app_id) || app_id.empty()) | |
| 606 return false; | |
| 607 | |
| 608 extension_misc::LaunchContainer launch_container; | |
| 609 const Extension* extension; | |
| 610 if (!GetAppLaunchContainer(profile, app_id, &extension, &launch_container)) | |
| 611 return false; | |
| 612 | |
| 613 // If the user doesn't want to open a tab, fail. | |
| 614 if (launch_container != extension_misc::LAUNCH_TAB) | |
| 615 return false; | |
| 616 | |
| 617 RecordCmdLineAppHistogram(); | |
| 618 | |
| 619 WebContents* app_tab = Browser::OpenApplicationTab(profile, extension, GURL(), | |
| 620 NEW_FOREGROUND_TAB); | |
| 621 return (app_tab != NULL); | |
| 622 } | |
| 623 | |
| 624 bool BrowserInit::LaunchWithProfile::OpenApplicationWindow(Profile* profile) { | |
| 625 std::string url_string, app_id; | |
| 626 if (!IsAppLaunch(&url_string, &app_id)) | |
| 627 return false; | |
| 628 | |
| 629 // This can fail if the app_id is invalid. It can also fail if the | |
| 630 // extension is external, and has not yet been installed. | |
| 631 // TODO(skerner): Do something reasonable here. Pop up a warning panel? | |
| 632 // Open an URL to the gallery page of the extension id? | |
| 633 if (!app_id.empty()) { | |
| 634 extension_misc::LaunchContainer launch_container; | |
| 635 const Extension* extension; | |
| 636 if (!GetAppLaunchContainer(profile, app_id, &extension, &launch_container)) | |
| 637 return false; | |
| 638 | |
| 639 // TODO(skerner): Could pass in |extension| and |launch_container|, | |
| 640 // and avoid calling GetAppLaunchContainer() both here and in | |
| 641 // OpenApplicationTab(). | |
| 642 | |
| 643 if (launch_container == extension_misc::LAUNCH_TAB) | |
| 644 return false; | |
| 645 | |
| 646 RecordCmdLineAppHistogram(); | |
| 647 WebContents* tab_in_app_window = Browser::OpenApplication( | |
| 648 profile, extension, launch_container, GURL(), NEW_WINDOW); | |
| 649 // Platform apps fire off a launch event which may or may not open a window. | |
| 650 return (tab_in_app_window != NULL || extension->is_platform_app()); | |
| 651 } | |
| 652 | |
| 653 if (url_string.empty()) | |
| 654 return false; | |
| 655 | |
| 656 #if defined(OS_WIN) // Fix up Windows shortcuts. | |
| 657 ReplaceSubstringsAfterOffset(&url_string, 0, "\\x", "%"); | |
| 658 #endif | |
| 659 GURL url(url_string); | |
| 660 | |
| 661 // Restrict allowed URLs for --app switch. | |
| 662 if (!url.is_empty() && url.is_valid()) { | |
| 663 ChildProcessSecurityPolicy *policy = | |
| 664 ChildProcessSecurityPolicy::GetInstance(); | |
| 665 if (policy->IsWebSafeScheme(url.scheme()) || | |
| 666 url.SchemeIs(chrome::kFileScheme)) { | |
| 667 | |
| 668 if (profile->GetExtensionService()->IsInstalledApp(url)) { | |
| 669 RecordCmdLineAppHistogram(); | |
| 670 } else { | |
| 671 AppLauncherHandler::RecordAppLaunchType( | |
| 672 extension_misc::APP_LAUNCH_CMD_LINE_APP_LEGACY); | |
| 673 } | |
| 674 WebContents* app_tab = Browser::OpenAppShortcutWindow( | |
| 675 profile, | |
| 676 url, | |
| 677 true); // Update app info. | |
| 678 return (app_tab != NULL); | |
| 679 } | |
| 680 } | |
| 681 return false; | |
| 682 } | |
| 683 | |
| 684 void BrowserInit::LaunchWithProfile::ProcessLaunchURLs( | |
| 685 bool process_startup, | |
| 686 const std::vector<GURL>& urls_to_open) { | |
| 687 // If we're starting up in "background mode" (no open browser window) then | |
| 688 // don't open any browser windows, unless kAutoLaunchAtStartup is also | |
| 689 // specified. | |
| 690 if (process_startup && | |
| 691 command_line_.HasSwitch(switches::kNoStartupWindow) && | |
| 692 !command_line_.HasSwitch(switches::kAutoLaunchAtStartup)) { | |
| 693 return; | |
| 694 } | |
| 695 | |
| 696 if (process_startup && ProcessStartupURLs(urls_to_open)) { | |
| 697 // ProcessStartupURLs processed the urls, nothing else to do. | |
| 698 return; | |
| 699 } | |
| 700 | |
| 701 IsProcessStartup is_process_startup = process_startup ? | |
| 702 IS_PROCESS_STARTUP : IS_NOT_PROCESS_STARTUP; | |
| 703 if (!process_startup) { | |
| 704 // Even if we're not starting a new process, this may conceptually be | |
| 705 // "startup" for the user and so should be handled in a similar way. Eg., | |
| 706 // Chrome may have been running in the background due to an app with a | |
| 707 // background page being installed, or running with only an app window | |
| 708 // displayed. | |
| 709 SessionService* service = SessionServiceFactory::GetForProfile(profile_); | |
| 710 if (service && service->ShouldNewWindowStartSession()) { | |
| 711 // Restore the last session if any. | |
| 712 if (!HasPendingUncleanExit(profile_) && | |
| 713 service->RestoreIfNecessary(urls_to_open)) { | |
| 714 return; | |
| 715 } | |
| 716 // Open user-specified URLs like pinned tabs and startup tabs. | |
| 717 Browser* browser = ProcessSpecifiedURLs(urls_to_open); | |
| 718 if (browser) { | |
| 719 AddInfoBarsIfNecessary(browser, is_process_startup); | |
| 720 return; | |
| 721 } | |
| 722 } | |
| 723 } | |
| 724 | |
| 725 // Session startup didn't occur, open the urls. | |
| 726 | |
| 727 Browser* browser = NULL; | |
| 728 std::vector<GURL> adjust_urls = urls_to_open; | |
| 729 if (adjust_urls.empty()) | |
| 730 AddStartupURLs(&adjust_urls); | |
| 731 else if (!command_line_.HasSwitch(switches::kOpenInNewWindow)) | |
| 732 browser = BrowserList::GetLastActiveWithProfile(profile_); | |
| 733 | |
| 734 // This will launch a browser; prevent session restore. | |
| 735 in_synchronous_profile_launch = true; | |
| 736 browser = OpenURLsInBrowser(browser, process_startup, adjust_urls); | |
| 737 in_synchronous_profile_launch = false; | |
| 738 AddInfoBarsIfNecessary(browser, is_process_startup); | |
| 739 } | |
| 740 | |
| 741 bool BrowserInit::LaunchWithProfile::ProcessStartupURLs( | |
| 742 const std::vector<GURL>& urls_to_open) { | |
| 743 SessionStartupPref pref = GetSessionStartupPref(command_line_, profile_); | |
| 744 | |
| 745 if (pref.type == SessionStartupPref::LAST) { | |
| 746 if (!profile_->DidLastSessionExitCleanly() && | |
| 747 !command_line_.HasSwitch(switches::kRestoreLastSession)) { | |
| 748 // The last session crashed. It's possible automatically loading the | |
| 749 // page will trigger another crash, locking the user out of chrome. | |
| 750 // To avoid this, don't restore on startup but instead show the crashed | |
| 751 // infobar. | |
| 752 return false; | |
| 753 } | |
| 754 | |
| 755 uint32 restore_behavior = SessionRestore::SYNCHRONOUS | | |
| 756 SessionRestore::ALWAYS_CREATE_TABBED_BROWSER; | |
| 757 #if defined(OS_MACOSX) | |
| 758 // On Mac, when restoring a session with no windows, suppress the creation | |
| 759 // of a new window in the case where the system is launching Chrome via a | |
| 760 // login item or Lion's resume feature. | |
| 761 if (base::mac::WasLaunchedAsLoginOrResumeItem()) { | |
| 762 restore_behavior = restore_behavior & | |
| 763 ~SessionRestore::ALWAYS_CREATE_TABBED_BROWSER; | |
| 764 } | |
| 765 #endif | |
| 766 | |
| 767 Browser* browser = SessionRestore::RestoreSession(profile_, | |
| 768 NULL, | |
| 769 restore_behavior, | |
| 770 urls_to_open); | |
| 771 AddInfoBarsIfNecessary(browser, IS_PROCESS_STARTUP); | |
| 772 return true; | |
| 773 } | |
| 774 | |
| 775 Browser* browser = ProcessSpecifiedURLs(urls_to_open); | |
| 776 if (!browser) | |
| 777 return false; | |
| 778 | |
| 779 AddInfoBarsIfNecessary(browser, IS_PROCESS_STARTUP); | |
| 780 return true; | |
| 781 } | |
| 782 | |
| 783 Browser* BrowserInit::LaunchWithProfile::ProcessSpecifiedURLs( | |
| 784 const std::vector<GURL>& urls_to_open) { | |
| 785 SessionStartupPref pref = GetSessionStartupPref(command_line_, profile_); | |
| 786 std::vector<Tab> tabs; | |
| 787 // Pinned tabs should not be displayed when chrome is launched in incognito | |
| 788 // mode. Also, no pages should be opened automatically if the session | |
| 789 // crashed. Otherwise it might trigger another crash, locking the user out of | |
| 790 // chrome. The crash infobar is shown in this case. | |
| 791 if (!IncognitoModePrefs::ShouldLaunchIncognito(command_line_, | |
| 792 profile_->GetPrefs()) && | |
| 793 !HasPendingUncleanExit(profile_)) { | |
| 794 tabs = PinnedTabCodec::ReadPinnedTabs(profile_); | |
| 795 } | |
| 796 | |
| 797 RecordAppLaunches(profile_, urls_to_open, tabs); | |
| 798 | |
| 799 if (!urls_to_open.empty()) { | |
| 800 // If urls were specified on the command line, use them. | |
| 801 UrlsToTabs(urls_to_open, &tabs); | |
| 802 } else if (pref.type == SessionStartupPref::URLS && !pref.urls.empty() && | |
| 803 !HasPendingUncleanExit(profile_)) { | |
| 804 // Only use the set of urls specified in preferences if nothing was | |
| 805 // specified on the command line. Filter out any urls that are to be | |
| 806 // restored by virtue of having been previously pinned. | |
| 807 AddUniqueURLs(pref.urls, &tabs); | |
| 808 } else if (pref.type == SessionStartupPref::DEFAULT) { | |
| 809 std::vector<GURL> urls; | |
| 810 AddStartupURLs(&urls); | |
| 811 UrlsToTabs(urls, &tabs); | |
| 812 | |
| 813 } else if (pref.type == SessionStartupPref::HOMEPAGE) { | |
| 814 // If 'homepage' selected, either by the user or by a policy, we should | |
| 815 // have migrated them to another value. | |
| 816 NOTREACHED() << "SessionStartupPref has deprecated type HOMEPAGE"; | |
| 817 } | |
| 818 | |
| 819 if (tabs.empty()) | |
| 820 return NULL; | |
| 821 | |
| 822 Browser* browser = OpenTabsInBrowser(NULL, true, tabs); | |
| 823 return browser; | |
| 824 } | |
| 825 | |
| 826 void BrowserInit::LaunchWithProfile::AddUniqueURLs( | |
| 827 const std::vector<GURL>& urls, | |
| 828 std::vector<Tab>* tabs) { | |
| 829 size_t num_existing_tabs = tabs->size(); | |
| 830 for (size_t i = 0; i < urls.size(); ++i) { | |
| 831 bool in_tabs = false; | |
| 832 for (size_t j = 0; j < num_existing_tabs; ++j) { | |
| 833 if (urls[i] == (*tabs)[j].url) { | |
| 834 in_tabs = true; | |
| 835 break; | |
| 836 } | |
| 837 } | |
| 838 if (!in_tabs) { | |
| 839 BrowserInit::LaunchWithProfile::Tab tab; | |
| 840 tab.is_pinned = false; | |
| 841 tab.url = urls[i]; | |
| 842 tabs->push_back(tab); | |
| 843 } | |
| 844 } | |
| 845 } | |
| 846 | |
| 847 Browser* BrowserInit::LaunchWithProfile::OpenURLsInBrowser( | |
| 848 Browser* browser, | |
| 849 bool process_startup, | |
| 850 const std::vector<GURL>& urls) { | |
| 851 std::vector<Tab> tabs; | |
| 852 UrlsToTabs(urls, &tabs); | |
| 853 return OpenTabsInBrowser(browser, process_startup, tabs); | |
| 854 } | |
| 855 | |
| 856 Browser* BrowserInit::LaunchWithProfile::OpenTabsInBrowser( | |
| 857 Browser* browser, | |
| 858 bool process_startup, | |
| 859 const std::vector<Tab>& tabs) { | |
| 860 DCHECK(!tabs.empty()); | |
| 861 | |
| 862 // If we don't yet have a profile, try to use the one we're given from | |
| 863 // |browser|. While we may not end up actually using |browser| (since it | |
| 864 // could be a popup window), we can at least use the profile. | |
| 865 if (!profile_ && browser) | |
| 866 profile_ = browser->profile(); | |
| 867 | |
| 868 if (!browser || !browser->is_type_tabbed()) { | |
| 869 browser = Browser::Create(profile_); | |
| 870 } else { | |
| 871 #if defined(TOOLKIT_GTK) | |
| 872 // Setting the time of the last action on the window here allows us to steal | |
| 873 // focus, which is what the user wants when opening a new tab in an existing | |
| 874 // browser window. | |
| 875 gtk_util::SetWMLastUserActionTime(browser->window()->GetNativeHandle()); | |
| 876 #endif | |
| 877 } | |
| 878 | |
| 879 #if !defined(OS_MACOSX) | |
| 880 // In kiosk mode, we want to always be fullscreen, so switch to that now. | |
| 881 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode)) | |
| 882 browser->ToggleFullscreenMode(); | |
| 883 #endif | |
| 884 | |
| 885 bool first_tab = true; | |
| 886 for (size_t i = 0; i < tabs.size(); ++i) { | |
| 887 // We skip URLs that we'd have to launch an external protocol handler for. | |
| 888 // This avoids us getting into an infinite loop asking ourselves to open | |
| 889 // a URL, should the handler be (incorrectly) configured to be us. Anyone | |
| 890 // asking us to open such a URL should really ask the handler directly. | |
| 891 bool handled_by_chrome = ProfileIOData::IsHandledURL(tabs[i].url) || | |
| 892 (profile_ && profile_->GetProtocolHandlerRegistry()->IsHandledProtocol( | |
| 893 tabs[i].url.scheme())); | |
| 894 if (!process_startup && !handled_by_chrome) | |
| 895 continue; | |
| 896 | |
| 897 int add_types = first_tab ? TabStripModel::ADD_ACTIVE : | |
| 898 TabStripModel::ADD_NONE; | |
| 899 add_types |= TabStripModel::ADD_FORCE_INDEX; | |
| 900 if (tabs[i].is_pinned) | |
| 901 add_types |= TabStripModel::ADD_PINNED; | |
| 902 int index = browser->GetIndexForInsertionDuringRestore(i); | |
| 903 | |
| 904 browser::NavigateParams params(browser, tabs[i].url, | |
| 905 content::PAGE_TRANSITION_START_PAGE); | |
| 906 params.disposition = first_tab ? NEW_FOREGROUND_TAB : | |
| 907 NEW_BACKGROUND_TAB; | |
| 908 params.tabstrip_index = index; | |
| 909 params.tabstrip_add_types = add_types; | |
| 910 params.extension_app_id = tabs[i].app_id; | |
| 911 browser::Navigate(¶ms); | |
| 912 | |
| 913 first_tab = false; | |
| 914 } | |
| 915 if (!browser->GetSelectedWebContents()) { | |
| 916 // TODO: this is a work around for 110909. Figure out why it's needed. | |
| 917 if (!browser->tab_count()) | |
| 918 browser->AddBlankTab(true); | |
| 919 else | |
| 920 browser->ActivateTabAt(0, false); | |
| 921 } | |
| 922 | |
| 923 browser->window()->Show(); | |
| 924 // TODO(jcampan): http://crbug.com/8123 we should not need to set the initial | |
| 925 // focus explicitly. | |
| 926 browser->GetSelectedWebContents()->GetView()->SetInitialFocus(); | |
| 927 | |
| 928 return browser; | |
| 929 } | |
| 930 | |
| 931 void BrowserInit::LaunchWithProfile::AddInfoBarsIfNecessary( | |
| 932 Browser* browser, | |
| 933 IsProcessStartup is_process_startup) { | |
| 934 if (!browser || !profile_ || browser->tab_count() == 0) | |
| 935 return; | |
| 936 | |
| 937 if (HasPendingUncleanExit(browser->profile())) | |
| 938 browser::ShowSessionCrashedPrompt(browser); | |
| 939 | |
| 940 // The bad flags info bar and the obsolete system info bar are only added to | |
| 941 // the first profile which is launched. Other profiles might be restoring the | |
| 942 // browsing sessions asynchronously, so we cannot add the info bars to the | |
| 943 // focused tabs here. | |
| 944 if (is_process_startup == IS_PROCESS_STARTUP) { | |
| 945 browser::ShowBadFlagsPrompt(browser); | |
| 946 browser::ShowObsoleteOSPrompt(browser); | |
| 947 } | |
| 948 } | |
| 949 | |
| 950 | |
| 951 void BrowserInit::LaunchWithProfile::AddStartupURLs( | |
| 952 std::vector<GURL>* startup_urls) const { | |
| 953 // If we have urls specified beforehand (i.e. from command line) use them | |
| 954 // and nothing else. | |
| 955 if (!startup_urls->empty()) | |
| 956 return; | |
| 957 | |
| 958 // If we have urls specified by the first run master preferences use them | |
| 959 // and nothing else. | |
| 960 if (browser_init_) { | |
| 961 if (!browser_init_->first_run_tabs_.empty()) { | |
| 962 std::vector<GURL>::iterator it = browser_init_->first_run_tabs_.begin(); | |
| 963 while (it != browser_init_->first_run_tabs_.end()) { | |
| 964 // Replace magic names for the actual urls. | |
| 965 if (it->host() == "new_tab_page") { | |
| 966 startup_urls->push_back(GURL(chrome::kChromeUINewTabURL)); | |
| 967 } else if (it->host() == "welcome_page") { | |
| 968 startup_urls->push_back(GetWelcomePageURL()); | |
| 969 } else { | |
| 970 startup_urls->push_back(*it); | |
| 971 } | |
| 972 ++it; | |
| 973 } | |
| 974 browser_init_->first_run_tabs_.clear(); | |
| 975 } | |
| 976 } | |
| 977 | |
| 978 // Otherwise open at least the new tab page (and the welcome page, if this | |
| 979 // is the first time the browser is being started), or the set of URLs | |
| 980 // specified on the command line. | |
| 981 if (startup_urls->empty()) { | |
| 982 startup_urls->push_back(GURL(chrome::kChromeUINewTabURL)); | |
| 983 PrefService* prefs = g_browser_process->local_state(); | |
| 984 if (prefs->FindPreference(prefs::kShouldShowWelcomePage) && | |
| 985 prefs->GetBoolean(prefs::kShouldShowWelcomePage)) { | |
| 986 // Reset the preference so we don't show the welcome page next time. | |
| 987 prefs->ClearPref(prefs::kShouldShowWelcomePage); | |
| 988 startup_urls->push_back(GetWelcomePageURL()); | |
| 989 } | |
| 990 } | |
| 991 | |
| 992 // If the sync promo page is going to be displayed then insert it at the front | |
| 993 // of the list. | |
| 994 if (SyncPromoUI::ShouldShowSyncPromoAtStartup(profile_, is_first_run_)) { | |
| 995 SyncPromoUI::DidShowSyncPromoAtStartup(profile_); | |
| 996 GURL old_url = (*startup_urls)[0]; | |
| 997 (*startup_urls)[0] = | |
| 998 SyncPromoUI::GetSyncPromoURL(GURL(chrome::kChromeUINewTabURL), | |
| 999 SyncPromoUI::SOURCE_START_PAGE); | |
| 1000 | |
| 1001 // An empty URL means to go to the home page. | |
| 1002 if (old_url.is_empty() && | |
| 1003 profile_->GetHomePage() == GURL(chrome::kChromeUINewTabURL)) { | |
| 1004 old_url = GURL(chrome::kChromeUINewTabURL); | |
| 1005 } | |
| 1006 | |
| 1007 // If the old URL is not the NTP then insert it right after the sync promo. | |
| 1008 if (old_url != GURL(chrome::kChromeUINewTabURL)) | |
| 1009 startup_urls->insert(startup_urls->begin() + 1, old_url); | |
| 1010 | |
| 1011 // If we have more than two startup tabs then skip the welcome page. | |
| 1012 if (startup_urls->size() > 2) { | |
| 1013 std::vector<GURL>::iterator it = std::find( | |
| 1014 startup_urls->begin(), startup_urls->end(), GetWelcomePageURL()); | |
| 1015 if (it != startup_urls->end()) | |
| 1016 startup_urls->erase(it); | |
| 1017 } | |
| 1018 } | |
| 1019 } | |
| 1020 | |
| 1021 void BrowserInit::LaunchWithProfile::CheckPreferencesBackup(Profile* profile) { | |
| 1022 ProtectorService* protector_service = | |
| 1023 ProtectorServiceFactory::GetForProfile(profile); | |
| 1024 ProtectedPrefsWatcher* prefs_watcher = protector_service->GetPrefsWatcher(); | |
| 1025 | |
| 1026 // Check if backup is valid. | |
| 1027 if (!prefs_watcher->is_backup_valid()) { | |
| 1028 protector_service->ShowChange(protector::CreatePrefsBackupInvalidChange()); | |
| 1029 // Further checks make no sense. | |
| 1030 return; | |
| 1031 } | |
| 1032 | |
| 1033 // Check for session startup (including pinned tabs) changes. | |
| 1034 if (SessionStartupPref::DidStartupPrefChange(profile) || | |
| 1035 prefs_watcher->DidPrefChange(prefs::kPinnedTabs)) { | |
| 1036 LOG(WARNING) << "Session startup settings have changed"; | |
| 1037 SessionStartupPref new_pref = SessionStartupPref::GetStartupPref(profile); | |
| 1038 PinnedTabCodec::Tabs new_tabs = PinnedTabCodec::ReadPinnedTabs(profile); | |
| 1039 const base::Value* tabs_backup = | |
| 1040 prefs_watcher->GetBackupForPref(prefs::kPinnedTabs); | |
| 1041 protector_service->ShowChange(protector::CreateSessionStartupChange( | |
| 1042 new_pref, | |
| 1043 new_tabs, | |
| 1044 SessionStartupPref::GetStartupPrefBackup(profile), | |
| 1045 PinnedTabCodec::ReadPinnedTabs(tabs_backup))); | |
| 1046 } | |
| 1047 | |
| 1048 // Check for homepage changes. | |
| 1049 if (prefs_watcher->DidPrefChange(prefs::kHomePage) || | |
| 1050 prefs_watcher->DidPrefChange(prefs::kHomePageIsNewTabPage) || | |
| 1051 prefs_watcher->DidPrefChange(prefs::kShowHomeButton)) { | |
| 1052 LOG(WARNING) << "Homepage has changed"; | |
| 1053 PrefService* prefs = profile->GetPrefs(); | |
| 1054 std::string backup_homepage; | |
| 1055 bool backup_homepage_is_ntp; | |
| 1056 bool backup_show_home_button; | |
| 1057 if (!prefs_watcher->GetBackupForPref(prefs::kHomePage)-> | |
| 1058 GetAsString(&backup_homepage) || | |
| 1059 !prefs_watcher->GetBackupForPref(prefs::kHomePageIsNewTabPage)-> | |
| 1060 GetAsBoolean(&backup_homepage_is_ntp) || | |
| 1061 !prefs_watcher->GetBackupForPref(prefs::kShowHomeButton)-> | |
| 1062 GetAsBoolean(&backup_show_home_button)) { | |
| 1063 NOTREACHED(); | |
| 1064 } | |
| 1065 protector_service->ShowChange(protector::CreateHomepageChange( | |
| 1066 // New: | |
| 1067 prefs->GetString(prefs::kHomePage), | |
| 1068 prefs->GetBoolean(prefs::kHomePageIsNewTabPage), | |
| 1069 prefs->GetBoolean(prefs::kShowHomeButton), | |
| 1070 // Backup: | |
| 1071 backup_homepage, | |
| 1072 backup_homepage_is_ntp, | |
| 1073 backup_show_home_button)); | |
| 1074 } | |
| 1075 } | |
| 1076 | |
| 1077 std::vector<GURL> BrowserInit::GetURLsFromCommandLine( | |
| 1078 const CommandLine& command_line, | |
| 1079 const FilePath& cur_dir, | |
| 1080 Profile* profile) { | |
| 1081 std::vector<GURL> urls; | |
| 1082 const CommandLine::StringVector& params = command_line.GetArgs(); | |
| 1083 | |
| 1084 for (size_t i = 0; i < params.size(); ++i) { | |
| 1085 FilePath param = FilePath(params[i]); | |
| 1086 // Handle Vista way of searching - "? <search-term>" | |
| 1087 if (param.value().size() > 2 && | |
| 1088 param.value()[0] == '?' && param.value()[1] == ' ') { | |
| 1089 const TemplateURL* default_provider = | |
| 1090 TemplateURLServiceFactory::GetForProfile(profile)-> | |
| 1091 GetDefaultSearchProvider(); | |
| 1092 if (default_provider) { | |
| 1093 const TemplateURLRef& search_url = default_provider->url_ref(); | |
| 1094 DCHECK(search_url.SupportsReplacement()); | |
| 1095 string16 search_term = param.LossyDisplayName().substr(2); | |
| 1096 urls.push_back(GURL(search_url.ReplaceSearchTerms(search_term, | |
| 1097 TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, string16()))); | |
| 1098 continue; | |
| 1099 } | |
| 1100 } | |
| 1101 | |
| 1102 // Otherwise, fall through to treating it as a URL. | |
| 1103 | |
| 1104 // This will create a file URL or a regular URL. | |
| 1105 // This call can (in rare circumstances) block the UI thread. | |
| 1106 // Allow it until this bug is fixed. | |
| 1107 // http://code.google.com/p/chromium/issues/detail?id=60641 | |
| 1108 GURL url; | |
| 1109 { | |
| 1110 base::ThreadRestrictions::ScopedAllowIO allow_io; | |
| 1111 url = URLFixerUpper::FixupRelativeFile(cur_dir, param); | |
| 1112 } | |
| 1113 // Exclude dangerous schemes. | |
| 1114 if (url.is_valid()) { | |
| 1115 ChildProcessSecurityPolicy *policy = | |
| 1116 ChildProcessSecurityPolicy::GetInstance(); | |
| 1117 if (policy->IsWebSafeScheme(url.scheme()) || | |
| 1118 url.SchemeIs(chrome::kFileScheme) || | |
| 1119 #if defined(OS_CHROMEOS) | |
| 1120 // In ChromeOS, allow a settings page to be specified on the | |
| 1121 // command line. See ExistingUserController::OnLoginSuccess. | |
| 1122 (url.spec().find(chrome::kChromeUISettingsURL) == 0) || | |
| 1123 #endif | |
| 1124 (url.spec().compare(chrome::kAboutBlankURL) == 0)) { | |
| 1125 urls.push_back(url); | |
| 1126 } | |
| 1127 } | |
| 1128 } | |
| 1129 #if defined(OS_WIN) | |
| 1130 if (urls.empty()) { | |
| 1131 // If we are in Windows 8 metro mode and were launched as a result of the | |
| 1132 // search charm or via a url navigation in metro, then fetch the | |
| 1133 // corresponding url. | |
| 1134 GURL url = browser::GetURLToOpen(profile); | |
| 1135 if (url.is_valid()) | |
| 1136 urls.push_back(GURL(url)); | |
| 1137 } | |
| 1138 #endif // OS_WIN | |
| 1139 return urls; | |
| 1140 } | |
| 1141 | |
| 1142 bool BrowserInit::ProcessCmdLineImpl( | |
| 1143 const CommandLine& command_line, | |
| 1144 const FilePath& cur_dir, | |
| 1145 bool process_startup, | |
| 1146 Profile* last_used_profile, | |
| 1147 const Profiles& last_opened_profiles, | |
| 1148 int* return_code, | |
| 1149 BrowserInit* browser_init) { | |
| 1150 DCHECK(last_used_profile); | |
| 1151 if (process_startup) { | |
| 1152 if (command_line.HasSwitch(switches::kDisablePromptOnRepost)) | |
| 1153 content::NavigationController::DisablePromptOnRepost(); | |
| 1154 if (!command_line.HasSwitch(switches::kDisableComponentUpdate)) | |
| 1155 RegisterComponentsForUpdate(command_line); | |
| 1156 } | |
| 1157 | |
| 1158 bool silent_launch = false; | |
| 1159 | |
| 1160 #if defined(ENABLE_AUTOMATION) | |
| 1161 // Look for the testing channel ID ONLY during process startup | |
| 1162 if (process_startup && | |
| 1163 command_line.HasSwitch(switches::kTestingChannelID)) { | |
| 1164 std::string testing_channel_id = command_line.GetSwitchValueASCII( | |
| 1165 switches::kTestingChannelID); | |
| 1166 // TODO(sanjeevr) Check if we need to make this a singleton for | |
| 1167 // compatibility with the old testing code | |
| 1168 // If there are any extra parameters, we expect each one to generate a | |
| 1169 // new tab; if there are none then we get one homepage tab. | |
| 1170 int expected_tab_count = 1; | |
| 1171 if (command_line.HasSwitch(switches::kNoStartupWindow) && | |
| 1172 !command_line.HasSwitch(switches::kAutoLaunchAtStartup)) { | |
| 1173 expected_tab_count = 0; | |
| 1174 #if defined(OS_CHROMEOS) | |
| 1175 // kLoginManager will cause Chrome to start up with the ChromeOS login | |
| 1176 // screen instead of a browser window, so it won't load any tabs. | |
| 1177 } else if (command_line.HasSwitch(switches::kLoginManager)) { | |
| 1178 expected_tab_count = 0; | |
| 1179 #endif | |
| 1180 } else if (command_line.HasSwitch(switches::kRestoreLastSession)) { | |
| 1181 std::string restore_session_value( | |
| 1182 command_line.GetSwitchValueASCII(switches::kRestoreLastSession)); | |
| 1183 base::StringToInt(restore_session_value, &expected_tab_count); | |
| 1184 } else { | |
| 1185 std::vector<GURL> urls_to_open = GetURLsFromCommandLine( | |
| 1186 command_line, cur_dir, last_used_profile); | |
| 1187 expected_tab_count = | |
| 1188 std::max(1, static_cast<int>(urls_to_open.size())); | |
| 1189 } | |
| 1190 if (!CreateAutomationProvider<TestingAutomationProvider>( | |
| 1191 testing_channel_id, | |
| 1192 last_used_profile, | |
| 1193 static_cast<size_t>(expected_tab_count))) | |
| 1194 return false; | |
| 1195 } | |
| 1196 | |
| 1197 if (command_line.HasSwitch(switches::kAutomationClientChannelID)) { | |
| 1198 std::string automation_channel_id = command_line.GetSwitchValueASCII( | |
| 1199 switches::kAutomationClientChannelID); | |
| 1200 // If there are any extra parameters, we expect each one to generate a | |
| 1201 // new tab; if there are none then we have no tabs | |
| 1202 std::vector<GURL> urls_to_open = GetURLsFromCommandLine( | |
| 1203 command_line, cur_dir, last_used_profile); | |
| 1204 size_t expected_tabs = | |
| 1205 std::max(static_cast<int>(urls_to_open.size()), 0); | |
| 1206 if (expected_tabs == 0) | |
| 1207 silent_launch = true; | |
| 1208 | |
| 1209 if (command_line.HasSwitch(switches::kChromeFrame)) { | |
| 1210 #if !defined(USE_AURA) | |
| 1211 if (!CreateAutomationProvider<ChromeFrameAutomationProvider>( | |
| 1212 automation_channel_id, last_used_profile, expected_tabs)) | |
| 1213 return false; | |
| 1214 #endif | |
| 1215 } else { | |
| 1216 if (!CreateAutomationProvider<AutomationProvider>( | |
| 1217 automation_channel_id, last_used_profile, expected_tabs)) | |
| 1218 return false; | |
| 1219 } | |
| 1220 } | |
| 1221 #endif // defined(ENABLE_AUTOMATION) | |
| 1222 | |
| 1223 // If we have been invoked to display a desktop notification on behalf of | |
| 1224 // the service process, we do not want to open any browser windows. | |
| 1225 if (command_line.HasSwitch(switches::kNotifyCloudPrintTokenExpired)) { | |
| 1226 silent_launch = true; | |
| 1227 CloudPrintProxyServiceFactory::GetForProfile(last_used_profile)-> | |
| 1228 ShowTokenExpiredNotification(); | |
| 1229 } | |
| 1230 | |
| 1231 // If we are just displaying a print dialog we shouldn't open browser | |
| 1232 // windows. | |
| 1233 if (command_line.HasSwitch(switches::kCloudPrintFile) && | |
| 1234 print_dialog_cloud::CreatePrintDialogFromCommandLine(command_line)) { | |
| 1235 silent_launch = true; | |
| 1236 } | |
| 1237 | |
| 1238 // If we are checking the proxy enabled policy, don't open any windows. | |
| 1239 if (command_line.HasSwitch(switches::kCheckCloudPrintConnectorPolicy)) { | |
| 1240 silent_launch = true; | |
| 1241 if (CloudPrintProxyServiceFactory::GetForProfile(last_used_profile)-> | |
| 1242 EnforceCloudPrintConnectorPolicyAndQuit()) | |
| 1243 // Success, nothing more needs to be done, so return false to stop | |
| 1244 // launching and quit. | |
| 1245 return false; | |
| 1246 } | |
| 1247 | |
| 1248 if (command_line.HasSwitch(switches::kExplicitlyAllowedPorts)) { | |
| 1249 std::string allowed_ports = | |
| 1250 command_line.GetSwitchValueASCII(switches::kExplicitlyAllowedPorts); | |
| 1251 net::SetExplicitlyAllowedPorts(allowed_ports); | |
| 1252 } | |
| 1253 | |
| 1254 #if defined(OS_CHROMEOS) | |
| 1255 // The browser will be launched after the user logs in. | |
| 1256 if (command_line.HasSwitch(switches::kLoginManager) || | |
| 1257 command_line.HasSwitch(switches::kLoginPassword)) { | |
| 1258 silent_launch = true; | |
| 1259 } | |
| 1260 #endif | |
| 1261 | |
| 1262 #if defined(TOOLKIT_VIEWS) && defined(OS_LINUX) | |
| 1263 // Get a list of pointer-devices that should be treated as touch-devices. | |
| 1264 // This is primarily used for testing/debugging touch-event processing when a | |
| 1265 // touch-device isn't available. | |
| 1266 std::string touch_devices = | |
| 1267 command_line.GetSwitchValueASCII(switches::kTouchDevices); | |
| 1268 | |
| 1269 if (!touch_devices.empty()) { | |
| 1270 std::vector<std::string> devs; | |
| 1271 std::vector<unsigned int> device_ids; | |
| 1272 unsigned int devid; | |
| 1273 base::SplitString(touch_devices, ',', &devs); | |
| 1274 for (std::vector<std::string>::iterator iter = devs.begin(); | |
| 1275 iter != devs.end(); ++iter) { | |
| 1276 if (base::StringToInt(*iter, reinterpret_cast<int*>(&devid))) | |
| 1277 device_ids.push_back(devid); | |
| 1278 else | |
| 1279 DLOG(WARNING) << "Invalid touch-device id: " << *iter; | |
| 1280 } | |
| 1281 ui::TouchFactory::GetInstance()->SetTouchDeviceList(device_ids); | |
| 1282 } | |
| 1283 #endif | |
| 1284 | |
| 1285 // If we don't want to launch a new browser window or tab (in the case | |
| 1286 // of an automation request), we are done here. | |
| 1287 if (!silent_launch) { | |
| 1288 IsProcessStartup is_process_startup = process_startup ? | |
| 1289 IS_PROCESS_STARTUP : IS_NOT_PROCESS_STARTUP; | |
| 1290 IsFirstRun is_first_run = first_run::IsChromeFirstRun() ? | |
| 1291 IS_FIRST_RUN : IS_NOT_FIRST_RUN; | |
| 1292 // |last_opened_profiles| will be empty in the following circumstances: | |
| 1293 // - This is the first launch. |last_used_profile| is the initial profile. | |
| 1294 // - The user exited the browser by closing all windows for all | |
| 1295 // profiles. |last_used_profile| is the profile which owned the last open | |
| 1296 // window. | |
| 1297 // - Only incognito windows were open when the browser exited. | |
| 1298 // |last_used_profile| is the last used incognito profile. Restoring it will | |
| 1299 // create a browser window for the corresponding original profile. | |
| 1300 if (last_opened_profiles.empty()) { | |
| 1301 if (!browser_init->LaunchBrowser(command_line, last_used_profile, cur_dir, | |
| 1302 is_process_startup, is_first_run, return_code)) | |
| 1303 return false; | |
| 1304 } else { | |
| 1305 // Launch the last used profile with the full command line, and the other | |
| 1306 // opened profiles without the URLs to launch. | |
| 1307 CommandLine command_line_without_urls(command_line.GetProgram()); | |
| 1308 const CommandLine::SwitchMap& switches = command_line.GetSwitches(); | |
| 1309 for (CommandLine::SwitchMap::const_iterator switch_it = switches.begin(); | |
| 1310 switch_it != switches.end(); ++switch_it) { | |
| 1311 command_line_without_urls.AppendSwitchNative(switch_it->first, | |
| 1312 switch_it->second); | |
| 1313 } | |
| 1314 // Launch the profiles in the order they became active. | |
| 1315 for (Profiles::const_iterator it = last_opened_profiles.begin(); | |
| 1316 it != last_opened_profiles.end(); ++it) { | |
| 1317 // Don't launch additional profiles which would only open a new tab | |
| 1318 // page. When restarting after an update, all profiles will reopen last | |
| 1319 // open pages. | |
| 1320 SessionStartupPref startup_pref = | |
| 1321 GetSessionStartupPref(command_line, *it); | |
| 1322 if (*it != last_used_profile && | |
| 1323 startup_pref.type == SessionStartupPref::DEFAULT && | |
| 1324 !HasPendingUncleanExit(*it)) | |
| 1325 continue; | |
| 1326 if (!browser_init->LaunchBrowser((*it == last_used_profile) ? | |
| 1327 command_line : command_line_without_urls, *it, cur_dir, | |
| 1328 is_process_startup, is_first_run, return_code)) | |
| 1329 return false; | |
| 1330 // We've launched at least one browser. | |
| 1331 is_process_startup = BrowserInit::IS_NOT_PROCESS_STARTUP; | |
| 1332 } | |
| 1333 } | |
| 1334 } | |
| 1335 return true; | |
| 1336 } | |
| 1337 | |
| 1338 template <class AutomationProviderClass> | |
| 1339 bool BrowserInit::CreateAutomationProvider(const std::string& channel_id, | |
| 1340 Profile* profile, | |
| 1341 size_t expected_tabs) { | |
| 1342 #if defined(ENABLE_AUTOMATION) | |
| 1343 scoped_refptr<AutomationProviderClass> automation = | |
| 1344 new AutomationProviderClass(profile); | |
| 1345 if (!automation->InitializeChannel(channel_id)) | |
| 1346 return false; | |
| 1347 automation->SetExpectedTabCount(expected_tabs); | |
| 1348 | |
| 1349 AutomationProviderList* list = g_browser_process->GetAutomationProviderList(); | |
| 1350 DCHECK(list); | |
| 1351 list->AddProvider(automation); | |
| 1352 #endif // defined(ENABLE_AUTOMATION) | |
| 1353 | |
| 1354 return true; | |
| 1355 } | |
| 1356 | |
| 1357 // static | |
| 1358 void BrowserInit::ProcessCommandLineOnProfileCreated( | |
| 1359 const CommandLine& cmd_line, | |
| 1360 const FilePath& cur_dir, | |
| 1361 Profile* profile, | |
| 1362 Profile::CreateStatus status) { | |
| 1363 if (status == Profile::CREATE_STATUS_INITIALIZED) | |
| 1364 ProcessCmdLineImpl(cmd_line, cur_dir, false, profile, Profiles(), NULL, | |
| 1365 NULL); | |
| 1366 } | |
| 1367 | |
| 1368 // static | |
| 1369 void BrowserInit::ProcessCommandLineAlreadyRunning(const CommandLine& cmd_line, | |
| 1370 const FilePath& cur_dir) { | |
| 1371 if (cmd_line.HasSwitch(switches::kProfileDirectory)) { | |
| 1372 ProfileManager* profile_manager = g_browser_process->profile_manager(); | |
| 1373 FilePath path = cmd_line.GetSwitchValuePath(switches::kProfileDirectory); | |
| 1374 path = profile_manager->user_data_dir().Append(path); | |
| 1375 profile_manager->CreateProfileAsync(path, | |
| 1376 base::Bind(&BrowserInit::ProcessCommandLineOnProfileCreated, | |
| 1377 cmd_line, cur_dir)); | |
| 1378 return; | |
| 1379 } | |
| 1380 | |
| 1381 Profile* profile = ProfileManager::GetLastUsedProfile(); | |
| 1382 if (!profile) { | |
| 1383 // We should only be able to get here if the profile already exists and | |
| 1384 // has been created. | |
| 1385 NOTREACHED(); | |
| 1386 return; | |
| 1387 } | |
| 1388 ProcessCmdLineImpl(cmd_line, cur_dir, false, profile, Profiles(), NULL, NULL); | |
| 1389 } | |
| OLD | NEW |