browser_main.cc revision c407dc5cd9bdc5668497f21b26b09d988ab439de
1// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/browser_main.h"
6
7#include <algorithm>
8#include <string>
9#include <vector>
10
11#include "app/hi_res_timer_manager.h"
12#include "app/l10n_util.h"
13#include "app/resource_bundle.h"
14#include "app/system_monitor.h"
15#include "base/command_line.h"
16#include "base/field_trial.h"
17#include "base/file_util.h"
18#include "base/histogram.h"
19#include "base/scoped_nsautorelease_pool.h"
20#include "base/path_service.h"
21#include "base/platform_thread.h"
22#include "base/process_util.h"
23#include "base/string_piece.h"
24#include "base/string_util.h"
25#include "base/sys_string_conversions.h"
26#include "base/time.h"
27#include "base/values.h"
28#include "build/build_config.h"
29#include "chrome/browser/browser.h"
30#include "chrome/browser/browser_main_win.h"
31#include "chrome/browser/browser_init.h"
32#include "chrome/browser/browser_prefs.h"
33#include "chrome/browser/browser_process.h"
34#include "chrome/browser/browser_process_impl.h"
35#include "chrome/browser/browser_shutdown.h"
36#include "chrome/browser/chrome_thread.h"
37#include "chrome/browser/dom_ui/chrome_url_data_manager.h"
38#include "chrome/browser/extensions/extension_protocols.h"
39#include "chrome/browser/extensions/extensions_service.h"
40#include "chrome/browser/first_run.h"
41#include "chrome/browser/jankometer.h"
42#include "chrome/browser/metrics/histogram_synchronizer.h"
43#include "chrome/browser/metrics/metrics_log.h"
44#include "chrome/browser/metrics/metrics_service.h"
45#include "chrome/browser/net/predictor_api.h"
46#include "chrome/browser/net/metadata_url_request.h"
47#include "chrome/browser/net/sdch_dictionary_fetcher.h"
48#include "chrome/browser/net/websocket_experiment/websocket_experiment_runner.h"
49#include "chrome/browser/plugin_service.h"
50#include "chrome/browser/pref_service.h"
51#include "chrome/browser/pref_value_store.h"
52#include "chrome/browser/process_singleton.h"
53#include "chrome/browser/profile.h"
54#include "chrome/browser/profile_manager.h"
55#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
56#include "chrome/browser/search_engines/template_url_model.h"
57#include "chrome/browser/search_engines/template_url_prepopulate_data.h"
58#include "chrome/browser/shell_integration.h"
59#include "chrome/browser/translate/translate_manager.h"
60#include "chrome/common/child_process.h"
61#include "chrome/common/chrome_constants.h"
62#include "chrome/common/chrome_paths.h"
63#include "chrome/common/chrome_switches.h"
64#include "chrome/common/json_pref_store.h"
65#include "chrome/common/jstemplate_builder.h"
66#include "chrome/common/main_function_params.h"
67#include "chrome/common/net/net_resource_provider.h"
68#include "chrome/common/pref_names.h"
69#include "chrome/common/result_codes.h"
70#include "chrome/installer/util/google_update_settings.h"
71#include "chrome/installer/util/master_preferences.h"
72#include "grit/app_locale_settings.h"
73#include "grit/chromium_strings.h"
74#include "grit/generated_resources.h"
75#include "net/base/cookie_monster.h"
76#include "net/base/net_module.h"
77#include "net/base/network_change_notifier.h"
78#include "net/http/http_network_layer.h"
79#include "net/http/http_network_session.h"
80#include "net/http/http_network_transaction.h"
81#include "net/socket/client_socket_pool_base.h"
82#include "net/spdy/spdy_session_pool.h"
83
84#if defined(USE_LINUX_BREAKPAD)
85#include "base/linux_util.h"
86#include "chrome/app/breakpad_linux.h"
87#endif
88
89#if defined(OS_POSIX) && !defined(OS_MACOSX)
90#include "chrome/browser/gtk/gtk_util.h"
91#endif
92
93#if defined(OS_CHROMEOS)
94#include "chrome/browser/chromeos/boot_times_loader.h"
95#endif
96
97// TODO(port): several win-only methods have been pulled out of this, but
98// BrowserMain() as a whole needs to be broken apart so that it's usable by
99// other platforms. For now, it's just a stub. This is a serious work in
100// progress and should not be taken as an indication of a real refactoring.
101
102#if defined(OS_WIN)
103#include <windows.h>
104#include <commctrl.h>
105#include <shellapi.h>
106
107#include "app/l10n_util_win.h"
108#include "app/win_util.h"
109#include "base/registry.h"
110#include "base/win_util.h"
111#include "chrome/browser/browser.h"
112#include "chrome/browser/browser_trial.h"
113#include "chrome/browser/metrics/user_metrics.h"
114#include "chrome/browser/net/url_fixer_upper.h"
115#include "chrome/browser/rlz/rlz.h"
116#include "chrome/browser/views/user_data_dir_dialog.h"
117#include "chrome/common/env_vars.h"
118#include "chrome/common/sandbox_policy.h"
119#include "chrome/installer/util/helper.h"
120#include "chrome/installer/util/install_util.h"
121#include "chrome/installer/util/shell_util.h"
122#include "chrome/installer/util/version.h"
123#include "net/base/net_util.h"
124#include "net/base/sdch_manager.h"
125#include "net/socket/ssl_client_socket_nss_factory.h"
126#include "printing/printed_document.h"
127#include "sandbox/src/sandbox.h"
128#endif  // defined(OS_WIN)
129
130#if defined(OS_MACOSX)
131#include <Security/Security.h>
132#include "chrome/browser/cocoa/install_from_dmg.h"
133#include "net/socket/ssl_client_socket_mac_factory.h"
134#endif
135
136#if defined(OS_MACOSX) || defined(OS_WIN)
137#include "base/nss_util.h"
138#endif
139
140#if defined(TOOLKIT_VIEWS)
141#include "chrome/browser/views/chrome_views_delegate.h"
142#include "views/focus/accelerator_handler.h"
143#endif
144
145#if defined(OS_CHROMEOS)
146#include "chrome/browser/chromeos/cros/cros_library.h"
147#include "chrome/browser/chromeos/cros/screen_lock_library.h"
148#include "chrome/browser/chromeos/customization_document.h"
149#include "chrome/browser/chromeos/external_metrics.h"
150#include "chrome/browser/chromeos/login/screen_locker.h"
151#include "chrome/browser/chromeos/login/user_manager.h"
152#include "chrome/browser/views/browser_dialogs.h"
153#endif
154
155// BrowserMainParts ------------------------------------------------------------
156
157BrowserMainParts::BrowserMainParts(const MainFunctionParams& parameters)
158    : parameters_(parameters),
159      parsed_command_line_(parameters.command_line_) {
160}
161
162// BrowserMainParts: EarlyInitialization() and related -------------------------
163
164void BrowserMainParts::EarlyInitialization() {
165  PreEarlyInitialization();
166
167  ConnectionFieldTrial();
168  SocketTimeoutFieldTrial();
169  SpdyFieldTrial();
170  InitializeSSL();  // TODO(viettrungluu): move to platform-specific method(s)
171
172  PostEarlyInitialization();
173}
174
175// This is an A/B test for the maximum number of persistent connections per
176// host. Currently Chrome, Firefox, and IE8 have this value set at 6. Safari
177// uses 4, and Fasterfox (a plugin for Firefox that supposedly configures it to
178// run faster) uses 8. We would like to see how much of an effect this value has
179// on browsing. Too large a value might cause us to run into SYN flood detection
180// mechanisms.
181void BrowserMainParts::ConnectionFieldTrial() {
182  const FieldTrial::Probability kConnDivisor = 100;
183  const FieldTrial::Probability kConn16 = 10;  // 10% probability
184  const FieldTrial::Probability kRemainingConn = 30;  // 30% probability
185
186  scoped_refptr<FieldTrial> conn_trial =
187      new FieldTrial("ConnCountImpact", kConnDivisor);
188
189  const int conn_16 = conn_trial->AppendGroup("_conn_count_16", kConn16);
190  const int conn_4 = conn_trial->AppendGroup("_conn_count_4", kRemainingConn);
191  const int conn_8 = conn_trial->AppendGroup("_conn_count_8", kRemainingConn);
192  const int conn_6 = conn_trial->AppendGroup("_conn_count_6",
193      FieldTrial::kAllRemainingProbability);
194
195  const int conn_trial_grp = conn_trial->group();
196
197  if (conn_trial_grp == conn_4) {
198    net::HttpNetworkSession::set_max_sockets_per_group(4);
199  } else if (conn_trial_grp == conn_6) {
200    // This (6) is the current default value.
201    net::HttpNetworkSession::set_max_sockets_per_group(6);
202  } else if (conn_trial_grp == conn_8) {
203    net::HttpNetworkSession::set_max_sockets_per_group(8);
204  } else if (conn_trial_grp == conn_16) {
205    net::HttpNetworkSession::set_max_sockets_per_group(16);
206  } else {
207    NOTREACHED();
208  }
209}
210
211// A/B test for determining a value for unused socket timeout. Currently the
212// timeout defaults to 10 seconds. Having this value set too low won't allow us
213// to take advantage of idle sockets. Setting it to too high could possibly
214// result in more ERR_CONNECT_RESETs, requiring one RTT to receive the RST
215// packet and possibly another RTT to re-establish the connection.
216void BrowserMainParts::SocketTimeoutFieldTrial() {
217  const FieldTrial::Probability kIdleSktToDivisor = 100;  // Idle socket timeout
218  const FieldTrial::Probability kSktToProb = 25;  // 25% probability
219
220  scoped_refptr<FieldTrial> socket_timeout_trial =
221      new FieldTrial("IdleSktToImpact", kIdleSktToDivisor);
222
223  const int socket_timeout_5 =
224      socket_timeout_trial->AppendGroup("_idle_timeout_5", kSktToProb);
225  const int socket_timeout_10 =
226      socket_timeout_trial->AppendGroup("_idle_timeout_10", kSktToProb);
227  const int socket_timeout_20 =
228      socket_timeout_trial->AppendGroup("_idle_timeout_20", kSktToProb);
229  const int socket_timeout_60 =
230      socket_timeout_trial->AppendGroup("_idle_timeout_60",
231                                        FieldTrial::kAllRemainingProbability);
232
233  const int idle_to_trial_grp = socket_timeout_trial->group();
234
235  if (idle_to_trial_grp == socket_timeout_5) {
236    net::ClientSocketPool::set_unused_idle_socket_timeout(5);
237  } else if (idle_to_trial_grp == socket_timeout_10) {
238    // This (10 seconds) is the current default value.
239    net::ClientSocketPool::set_unused_idle_socket_timeout(10);
240  } else if (idle_to_trial_grp == socket_timeout_20) {
241    net::ClientSocketPool::set_unused_idle_socket_timeout(20);
242  } else if (idle_to_trial_grp == socket_timeout_60) {
243    net::ClientSocketPool::set_unused_idle_socket_timeout(60);
244  } else {
245    NOTREACHED();
246  }
247}
248
249// When --use-spdy not set, users will be in A/B test for spdy.
250// group A (npn_with_spdy): this means npn and spdy are enabled. In case server
251//                          supports spdy, browser will use spdy.
252// group B (npn_with_http): this means npn is enabled but spdy won't be used.
253//                          Http is still used for all requests.
254//           default group: no npn or spdy is involved. The "old" non-spdy
255//                          chrome behavior.
256void BrowserMainParts::SpdyFieldTrial() {
257  bool is_spdy_trial = false;
258  if (parsed_command_line().HasSwitch(switches::kUseSpdy)) {
259    std::string spdy_mode =
260        parsed_command_line().GetSwitchValueASCII(switches::kUseSpdy);
261    net::HttpNetworkLayer::EnableSpdy(spdy_mode);
262  } else {
263    const FieldTrial::Probability kSpdyDivisor = 1000;
264    // To enable 100% npn_with_spdy, set npnhttp_probability = 0 and set
265    // npnspdy_probability = FieldTrial::kAllRemainingProbability.
266    // To collect stats, make sure that FieldTrial are distributed among
267    // all the three groups:
268    // npn_with_spdy : 50%, npn_with_http : 25%, default (no npn, no spdy): 25%.
269    // a. npn_with_spdy and default: these are used to collect stats for
270    //    alternate protocol with spdy vs. no alternate protocol case.
271    // b. npn_with_spdy and npn_with_http: these are used to collect stats for
272    //    https vs. https over spdy case.
273    FieldTrial::Probability npnhttp_probability = 250;
274    FieldTrial::Probability npnspdy_probability = 500;
275    scoped_refptr<FieldTrial> trial =
276        new FieldTrial("SpdyImpact", kSpdyDivisor);
277    // npn with only http support, no spdy.
278    int npn_http_grp =
279        trial->AppendGroup("_npn_with_http", npnhttp_probability);
280    // npn with spdy support.
281    int npn_spdy_grp =
282        trial->AppendGroup("_npn_with_spdy", npnspdy_probability);
283    int trial_grp = trial->group();
284    if (trial_grp == npn_http_grp) {
285      is_spdy_trial = true;
286      net::HttpNetworkLayer::EnableSpdy("npn-http");
287    } else if (trial_grp == npn_spdy_grp) {
288      is_spdy_trial = true;
289      net::HttpNetworkLayer::EnableSpdy("npn");
290    } else {
291      CHECK(!is_spdy_trial);
292    }
293  }
294}
295
296// TODO(viettrungluu): move to platform-specific methods
297void BrowserMainParts::InitializeSSL() {
298  // Use NSS for SSL by default.
299#if defined(OS_MACOSX)
300  // The default client socket factory uses NSS for SSL by default on Mac.
301  if (parsed_command_line().HasSwitch(switches::kUseSystemSSL)) {
302    net::ClientSocketFactory::SetSSLClientSocketFactory(
303        net::SSLClientSocketMacFactory);
304  } else {
305    // We want to be sure to init NSPR on the main thread.
306    base::EnsureNSPRInit();
307  }
308#elif defined(OS_WIN)
309  // Because of a build system issue (http://crbug.com/43461), the default
310  // client socket factory uses SChannel (the system SSL library) for SSL by
311  // default on Windows.
312  if (!parsed_command_line().HasSwitch(switches::kUseSystemSSL)) {
313    net::ClientSocketFactory::SetSSLClientSocketFactory(
314        net::SSLClientSocketNSSFactory);
315    // We want to be sure to init NSPR on the main thread.
316    base::EnsureNSPRInit();
317  }
318#endif
319}
320
321// -----------------------------------------------------------------------------
322// TODO(viettrungluu): move more/rest of BrowserMain() into above structure
323
324namespace {
325
326// This function provides some ways to test crash and assertion handling
327// behavior of the program.
328void HandleTestParameters(const CommandLine& command_line) {
329  // This parameter causes an assertion.
330  if (command_line.HasSwitch(switches::kBrowserAssertTest)) {
331    DCHECK(false);
332  }
333
334  // This parameter causes a null pointer crash (crash reporter trigger).
335  if (command_line.HasSwitch(switches::kBrowserCrashTest)) {
336    int* bad_pointer = NULL;
337    *bad_pointer = 0;
338  }
339
340#if defined(OS_CHROMEOS)
341  // Test loading libcros and exit. We return 0 if the library could be loaded,
342  // and 1 if it can't be. This is for validation that the library is installed
343  // and versioned properly for Chrome to find.
344  if (command_line.HasSwitch(switches::kTestLoadLibcros))
345    exit(!chromeos::CrosLibrary::Get()->EnsureLoaded());
346#endif
347}
348
349void RunUIMessageLoop(BrowserProcess* browser_process) {
350#if defined(TOOLKIT_VIEWS)
351  views::AcceleratorHandler accelerator_handler;
352  MessageLoopForUI::current()->Run(&accelerator_handler);
353#elif defined(USE_X11)
354  MessageLoopForUI::current()->Run(NULL);
355#elif defined(OS_POSIX)
356  MessageLoopForUI::current()->Run();
357#endif
358}
359
360void AddFirstRunNewTabs(BrowserInit* browser_init,
361                        const std::vector<GURL>& new_tabs) {
362  for (std::vector<GURL>::const_iterator it = new_tabs.begin();
363       it != new_tabs.end(); ++it) {
364    if (it->is_valid())
365      browser_init->AddFirstRunTab(*it);
366  }
367}
368
369#if defined(USE_LINUX_BREAKPAD)
370class GetLinuxDistroTask : public Task {
371 public:
372  explicit GetLinuxDistroTask() {}
373
374  virtual void Run() {
375    base::GetLinuxDistro();  // Initialize base::linux_distro if needed.
376  }
377
378  DISALLOW_COPY_AND_ASSIGN(GetLinuxDistroTask);
379};
380#endif  // USE_LINUX_BREAKPAD
381
382void InitializeNetworkOptions(const CommandLine& parsed_command_line) {
383  if (parsed_command_line.HasSwitch(switches::kEnableFileCookies)) {
384    // Enable cookie storage for file:// URLs.  Must do this before the first
385    // Profile (and therefore the first CookieMonster) is created.
386    net::CookieMonster::EnableFileScheme();
387  }
388
389  if (parsed_command_line.HasSwitch(switches::kFixedHttpPort)) {
390    net::HttpNetworkSession::set_fixed_http_port(StringToInt(
391        parsed_command_line.GetSwitchValueASCII(switches::kFixedHttpPort)));
392  }
393
394  if (parsed_command_line.HasSwitch(switches::kFixedHttpsPort)) {
395    net::HttpNetworkSession::set_fixed_https_port(StringToInt(
396        parsed_command_line.GetSwitchValueASCII(switches::kFixedHttpsPort)));
397  }
398
399  if (parsed_command_line.HasSwitch(switches::kIgnoreCertificateErrors))
400    net::HttpNetworkTransaction::IgnoreCertificateErrors(true);
401
402  if (parsed_command_line.HasSwitch(switches::kHostRules))
403    net::HttpNetworkTransaction::SetHostMappingRules(
404        parsed_command_line.GetSwitchValueASCII(switches::kHostRules));
405
406  if (parsed_command_line.HasSwitch(switches::kMaxSpdySessionsPerDomain)) {
407    int value = StringToInt(
408        parsed_command_line.GetSwitchValueASCII(
409            switches::kMaxSpdySessionsPerDomain));
410    net::SpdySessionPool::set_max_sessions_per_domain(value);
411  }
412}
413
414// Creates key child threads. We need to do this explicitly since
415// ChromeThread::PostTask silently deletes a posted task if the target message
416// loop isn't created.
417void CreateChildThreads(BrowserProcessImpl* process) {
418  process->db_thread();
419  process->file_thread();
420  process->process_launcher_thread();
421  process->cache_thread();
422  process->io_thread();
423}
424
425// Returns the new local state object, guaranteed non-NULL.
426PrefService* InitializeLocalState(const CommandLine& parsed_command_line,
427                                  bool is_first_run) {
428  FilePath local_state_path;
429  PathService::Get(chrome::FILE_LOCAL_STATE, &local_state_path);
430  bool local_state_file_exists = file_util::PathExists(local_state_path);
431
432  // Load local state.  This includes the application locale so we know which
433  // locale dll to load.
434  PrefService* local_state = g_browser_process->local_state();
435  DCHECK(local_state);
436
437  // Initialize ResourceBundle which handles files loaded from external
438  // sources. This has to be done before uninstall code path and before prefs
439  // are registered.
440  local_state->RegisterStringPref(prefs::kApplicationLocale, "");
441  local_state->RegisterBooleanPref(prefs::kMetricsReportingEnabled,
442      GoogleUpdateSettings::GetCollectStatsConsent());
443
444  if (is_first_run) {
445#if defined(OS_WIN)
446    // During first run we read the google_update registry key to find what
447    // language the user selected when downloading the installer. This
448    // becomes our default language in the prefs.
449    // Other platforms obey the system locale.
450    std::wstring install_lang;
451    if (GoogleUpdateSettings::GetLanguage(&install_lang)) {
452      local_state->SetString(prefs::kApplicationLocale,
453                             WideToASCII(install_lang));
454    }
455#endif  // defined(OS_WIN)
456  }
457
458  // If the local state file for the current profile doesn't exist and the
459  // parent profile command line flag is present, then we should inherit some
460  // local state from the parent profile.
461  // Checking that the local state file for the current profile doesn't exist
462  // is the most robust way to determine whether we need to inherit or not
463  // since the parent profile command line flag can be present even when the
464  // current profile is not a new one, and in that case we do not want to
465  // inherit and reset the user's setting.
466  if (!local_state_file_exists &&
467      parsed_command_line.HasSwitch(switches::kParentProfile)) {
468    FilePath parent_profile =
469        parsed_command_line.GetSwitchValuePath(switches::kParentProfile);
470    scoped_ptr<PrefService> parent_local_state(
471        PrefService::CreatePrefService(parent_profile));
472    parent_local_state->RegisterStringPref(prefs::kApplicationLocale,
473                                           std::string());
474    // Right now, we only inherit the locale setting from the parent profile.
475    local_state->SetString(
476        prefs::kApplicationLocale,
477        parent_local_state->GetString(prefs::kApplicationLocale));
478  }
479
480  return local_state;
481}
482
483// Windows-specific initialization code for the sandbox broker services. This
484// is just a NOP on non-Windows platforms to reduce ifdefs later on.
485void InitializeBrokerServices(const MainFunctionParams& parameters,
486                              const CommandLine& parsed_command_line) {
487#if defined(OS_WIN)
488  sandbox::BrokerServices* broker_services =
489      parameters.sandbox_info_.BrokerServices();
490  if (broker_services) {
491    sandbox::InitBrokerServices(broker_services);
492    if (!parsed_command_line.HasSwitch(switches::kNoSandbox)) {
493      bool use_winsta = !parsed_command_line.HasSwitch(
494                            switches::kDisableAltWinstation);
495      // Precreate the desktop and window station used by the renderers.
496      sandbox::TargetPolicy* policy = broker_services->CreatePolicy();
497      sandbox::ResultCode result = policy->CreateAlternateDesktop(use_winsta);
498      CHECK(sandbox::SBOX_ERROR_FAILED_TO_SWITCH_BACK_WINSTATION != result);
499      policy->Release();
500    }
501  }
502#endif
503}
504
505// Initializes the metrics service with the configuration for this process,
506// returning the created service (guaranteed non-NULL).
507MetricsService* InitializeMetrics(const CommandLine& parsed_command_line,
508                                  const PrefService* local_state) {
509#if defined(OS_WIN)
510  if (InstallUtil::IsChromeFrameProcess())
511    MetricsLog::set_version_extension("-F");
512#elif defined(ARCH_CPU_64_BITS)
513  MetricsLog::set_version_extension("-64");
514#endif  // defined(OS_WIN)
515
516  MetricsService* metrics = g_browser_process->metrics_service();
517
518  if (parsed_command_line.HasSwitch(switches::kMetricsRecordingOnly)) {
519    // If we're testing then we don't care what the user preference is, we turn
520    // on recording, but not reporting, otherwise tests fail.
521    metrics->StartRecordingOnly();
522  } else {
523    // If the user permits metrics reporting with the checkbox in the
524    // prefs, we turn on recording.  We disable metrics completely for
525    // non-official builds.
526#if defined(GOOGLE_CHROME_BUILD)
527    bool enabled = local_state->GetBoolean(prefs::kMetricsReportingEnabled);
528    metrics->SetUserPermitsUpload(enabled);
529    if (enabled) {
530      metrics->Start();
531      chrome_browser_net_websocket_experiment::
532          WebSocketExperimentRunner::Start();
533    }
534#endif
535  }
536
537  return metrics;
538}
539
540// Initializes the profile, possibly doing some user prompting to pick a
541// fallback profile. Returns the newly created profile, or NULL if startup
542// should not continue.
543Profile* CreateProfile(const MainFunctionParams& parameters,
544                       const FilePath& user_data_dir) {
545  Profile* profile = g_browser_process->profile_manager()->GetDefaultProfile(
546      user_data_dir);
547  if (profile)
548    return profile;
549
550#if defined(OS_WIN)
551  // Ideally, we should be able to run w/o access to disk.  For now, we
552  // prompt the user to pick a different user-data-dir and restart chrome
553  // with the new dir.
554  // http://code.google.com/p/chromium/issues/detail?id=11510
555  FilePath new_user_data_dir = UserDataDirDialog::RunUserDataDirDialog(
556      user_data_dir);
557  if (!parameters.ui_task && browser_shutdown::delete_resources_on_shutdown) {
558    // Only delete the resources if we're not running tests. If we're running
559    // tests the resources need to be reused as many places in the UI cache
560    // SkBitmaps from the ResourceBundle.
561    ResourceBundle::CleanupSharedInstance();
562  }
563
564  if (!new_user_data_dir.empty()) {
565    // Because of the way CommandLine parses, it's sufficient to append a new
566    // --user-data-dir switch.  The last flag of the same name wins.
567    // TODO(tc): It would be nice to remove the flag we don't want, but that
568    // sounds risky if we parse differently than CommandLineToArgvW.
569    CommandLine new_command_line = parameters.command_line_;
570    new_command_line.AppendSwitchWithValue(switches::kUserDataDir,
571                                           new_user_data_dir.ToWStringHack());
572    base::LaunchApp(new_command_line, false, false, NULL);
573  }
574#else
575  // TODO(port): fix this.  See comments near the definition of
576  // user_data_dir.  It is better to CHECK-fail here than it is to
577  // silently exit because of missing code in the above test.
578  CHECK(profile) << "Cannot get default profile.";
579#endif
580
581  return NULL;
582}
583
584#if defined(OS_WIN)
585
586// gfx::Font callbacks
587void AdjustUIFont(LOGFONT* logfont) {
588  l10n_util::AdjustUIFont(logfont);
589}
590
591int GetMinimumFontSize() {
592  return StringToInt(l10n_util::GetString(IDS_MINIMUM_UI_FONT_SIZE).c_str());
593}
594
595#endif
596
597#if defined(TOOLKIT_GTK)
598void InitializeToolkit() {
599  // It is important for this to happen before the first run dialog, as it
600  // styles the dialog as well.
601  gtk_util::InitRCStyles();
602}
603#elif defined(TOOLKIT_VIEWS)
604void InitializeToolkit() {
605  // The delegate needs to be set before any UI is created so that windows
606  // display the correct icon.
607  if (!views::ViewsDelegate::views_delegate)
608    views::ViewsDelegate::views_delegate = new ChromeViewsDelegate;
609
610#if defined(OS_WIN)
611  gfx::Font::adjust_font_callback = &AdjustUIFont;
612  gfx::Font::get_minimum_font_size_callback = &GetMinimumFontSize;
613
614  // Init common control sex.
615  INITCOMMONCONTROLSEX config;
616  config.dwSize = sizeof(config);
617  config.dwICC = ICC_WIN95_CLASSES;
618  InitCommonControlsEx(&config);
619#endif
620}
621#else
622void InitializeToolkit() {
623}
624#endif
625
626#if defined(OS_CHROMEOS)
627
628void OptionallyRunChromeOSLoginManager(const CommandLine& parsed_command_line) {
629  if (parsed_command_line.HasSwitch(switches::kLoginManager)) {
630    std::string first_screen =
631        parsed_command_line.GetSwitchValueASCII(switches::kLoginScreen);
632    std::string size_arg =
633        parsed_command_line.GetSwitchValueASCII(
634            switches::kLoginScreenSize);
635    gfx::Size size(0, 0);
636    // Allow the size of the login window to be set explicitly. If not set,
637    // default to the entire screen. This is mostly useful for testing.
638    if (size_arg.size()) {
639      std::vector<std::string> dimensions;
640      SplitString(size_arg, ',', &dimensions);
641      if (dimensions.size() == 2)
642        size.SetSize(StringToInt(dimensions[0]), StringToInt(dimensions[1]));
643    }
644    browser::ShowLoginWizard(first_screen, size);
645  }
646}
647
648bool OptionallyApplyServicesCustomizationFromCommandLine(
649    const CommandLine& parsed_command_line,
650    BrowserInit* browser_init) {
651  // For Chrome OS, we may need to fetch OEM partner's services customization
652  // manifest and apply the customizations. This happens on the very first run
653  // or if startup manifest is passed on the command line.
654  scoped_ptr<chromeos::ServicesCustomizationDocument> customization;
655  customization.reset(new chromeos::ServicesCustomizationDocument());
656  bool manifest_loaded = false;
657  if (parsed_command_line.HasSwitch(switches::kServicesManifest)) {
658    // Load manifest from file specified by command line switch.
659    FilePath manifest_path =
660        parsed_command_line.GetSwitchValuePath(switches::kServicesManifest);
661    manifest_loaded = customization->LoadManifestFromFile(manifest_path);
662    DCHECK(manifest_loaded) << manifest_path.value();
663  }
664  // If manifest was loaded successfully, apply the customizations.
665  if (manifest_loaded) {
666    browser_init->ApplyServicesCustomization(customization.get());
667  }
668  return manifest_loaded;
669}
670
671#else
672
673void OptionallyRunChromeOSLoginManager(const CommandLine& parsed_command_line) {
674  // Dummy empty function for non-ChromeOS builds to avoid extra ifdefs below.
675}
676
677bool OptionallyApplyServicesCustomizationFromCommandLine(
678    const CommandLine& parsed_command_line,
679    BrowserInit* browser_init) {
680  // Dummy empty function for non-ChromeOS builds to avoid extra ifdefs below.
681  return false;
682}
683
684#endif  // defined(OS_CHROMEOS)
685
686#if defined(OS_MACOSX)
687OSStatus KeychainCallback(SecKeychainEvent keychain_event,
688                          SecKeychainCallbackInfo *info, void *context) {
689  return noErr;
690}
691#endif
692
693}  // namespace
694
695#if defined(OS_WIN)
696#define DLLEXPORT __declspec(dllexport)
697
698// We use extern C for the prototype DLLEXPORT to avoid C++ name mangling.
699extern "C" {
700DLLEXPORT void __cdecl RelaunchChromeBrowserWithNewCommandLineIfNeeded();
701}
702
703DLLEXPORT void __cdecl RelaunchChromeBrowserWithNewCommandLineIfNeeded() {
704  Upgrade::RelaunchChromeBrowserWithNewCommandLineIfNeeded();
705}
706#endif
707
708// Main routine for running as the Browser process.
709int BrowserMain(const MainFunctionParams& parameters) {
710  scoped_ptr<BrowserMainParts>
711      parts(BrowserMainParts::CreateBrowserMainParts(parameters));
712
713  parts->EarlyInitialization();
714
715  // TODO(viettrungluu): put the remainder into BrowserMainParts
716  const CommandLine& parsed_command_line = parameters.command_line_;
717  base::ScopedNSAutoreleasePool* pool = parameters.autorelease_pool_;
718
719  // WARNING: If we get a WM_ENDSESSION objects created on the stack here
720  // are NOT deleted. If you need something to run during WM_ENDSESSION add it
721  // to browser_shutdown::Shutdown or BrowserProcess::EndSession.
722
723  // TODO(beng, brettw): someday, break this out into sub functions with well
724  //                     defined roles (e.g. pre/post-profile startup, etc).
725
726  // Do platform-specific things (such as finishing initializing Cocoa)
727  // prior to instantiating the message loop. This could be turned into a
728  // broadcast notification.
729  WillInitializeMainMessageLoop(parameters);
730
731  MessageLoop main_message_loop(MessageLoop::TYPE_UI);
732
733  SystemMonitor system_monitor;
734  HighResolutionTimerManager hi_res_timer_manager;
735  scoped_ptr<net::NetworkChangeNotifier> network_change_notifier(
736      net::NetworkChangeNotifier::Create());
737
738  const char* kThreadName = "CrBrowserMain";
739  PlatformThread::SetName(kThreadName);
740  main_message_loop.set_thread_name(kThreadName);
741
742  // Register the main thread by instantiating it, but don't call any methods.
743  ChromeThread main_thread(ChromeThread::UI, MessageLoop::current());
744
745  // TODO(viettrungluu): temporary while I refactor BrowserMain()
746  parts->TemporaryPosix_1();
747
748  FilePath user_data_dir;
749#if defined(OS_WIN)
750  PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
751#else
752  // Getting the user data dir can fail if the directory isn't
753  // creatable, for example; on Windows in code below we bring up a
754  // dialog prompting the user to pick a different directory.
755  // However, ProcessSingleton needs a real user_data_dir on Mac/Linux,
756  // so it's better to fail here than fail mysteriously elsewhere.
757  CHECK(PathService::Get(chrome::DIR_USER_DATA, &user_data_dir))
758      << "Must be able to get user data directory!";
759#endif
760
761  ProcessSingleton process_singleton(user_data_dir);
762
763  bool is_first_run = FirstRun::IsChromeFirstRun() ||
764      parsed_command_line.HasSwitch(switches::kFirstRun);
765
766  scoped_ptr<BrowserProcessImpl> browser_process;
767  if (parsed_command_line.HasSwitch(switches::kImport) ||
768      parsed_command_line.HasSwitch(switches::kImportFromFile)) {
769    // We use different BrowserProcess when importing so no GoogleURLTracker is
770    // instantiated (as it makes a URLRequest and we don't have an IO thread,
771    // see bug #1292702).
772    browser_process.reset(new FirstRunBrowserProcess(parsed_command_line));
773    is_first_run = false;
774  } else {
775    browser_process.reset(new BrowserProcessImpl(parsed_command_line));
776  }
777
778  // BrowserProcessImpl's constructor should set g_browser_process.
779  DCHECK(g_browser_process);
780
781  // This forces the TabCloseableStateWatcher to be created and, on chromeos,
782  // register for the notifications it needs to track the closeable state of
783  // tabs.
784  g_browser_process->tab_closeable_state_watcher();
785
786#if defined(USE_LINUX_BREAKPAD)
787  // Needs to be called after we have chrome::DIR_USER_DATA and
788  // g_browser_process.
789  g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE,
790      new GetLinuxDistroTask());
791  InitCrashReporter();
792#endif
793
794  // The broker service initialization needs to run early because it will
795  // initialize the sandbox broker, which requires the process to swap its
796  // window station. During this time all the UI will be broken. This has to
797  // run before threads and windows are created.
798  InitializeBrokerServices(parameters, parsed_command_line);
799
800  PrefService* local_state = InitializeLocalState(parsed_command_line,
801                                                  is_first_run);
802
803  InitializeToolkit();  // Must happen before we try to display any UI.
804
805  // If we're running tests (ui_task is non-null), then the ResourceBundle
806  // has already been initialized.
807  if (parameters.ui_task) {
808    g_browser_process->SetApplicationLocale("en-US");
809  } else {
810    // Mac starts it earlier in WillInitializeMainMessageLoop (because
811    // it is needed when loading the MainMenu.nib and the language doesn't
812    // depend on anything since it comes from Cocoa.
813#if defined(OS_MACOSX)
814    g_browser_process->SetApplicationLocale(l10n_util::GetLocaleOverride());
815#else
816    // On a POSIX OS other than ChromeOS, the parameter that is passed to the
817    // method InitSharedInstance is ignored.
818    std::string app_locale = ResourceBundle::InitSharedInstance(
819        ASCIIToWide(local_state->GetString(prefs::kApplicationLocale)));
820    g_browser_process->SetApplicationLocale(app_locale);
821
822    FilePath resources_pack_path;
823    PathService::Get(chrome::FILE_RESOURCES_PACK, &resources_pack_path);
824    ResourceBundle::AddDataPackToSharedInstance(resources_pack_path);
825#endif  // !defined(OS_MACOSX)
826  }
827
828#if defined(OS_POSIX) && !defined(OS_MACOSX)
829  gtk_util::SetDefaultWindowIcon();
830#endif
831
832  std::string try_chrome =
833      parsed_command_line.GetSwitchValueASCII(switches::kTryChromeAgain);
834  if (!try_chrome.empty()) {
835#if defined(OS_WIN)
836    Upgrade::TryResult answer =
837        Upgrade::ShowTryChromeDialog(StringToInt(try_chrome));
838    if (answer == Upgrade::TD_NOT_NOW)
839      return ResultCodes::NORMAL_EXIT_CANCEL;
840    if (answer == Upgrade::TD_UNINSTALL_CHROME)
841      return ResultCodes::NORMAL_EXIT_EXP2;
842#else
843    // We don't support retention experiments on Mac or Linux.
844    return ResultCodes::NORMAL_EXIT;
845#endif  // defined(OS_WIN)
846  }
847
848  BrowserInit browser_init;
849
850  // On first run, we need to process the predictor preferences before the
851  // browser's profile_manager object is created, but after ResourceBundle
852  // is initialized.
853  FirstRun::MasterPrefs master_prefs = { 0 };
854  bool first_run_ui_bypass = false;  // True to skip first run UI.
855  if (is_first_run) {
856    first_run_ui_bypass =
857        !FirstRun::ProcessMasterPreferences(user_data_dir, &master_prefs);
858    AddFirstRunNewTabs(&browser_init, master_prefs.new_tabs);
859
860    // If we are running in App mode, we do not want to show the importer
861    // (first run) UI.
862    if (!first_run_ui_bypass &&
863        (parsed_command_line.HasSwitch(switches::kApp) ||
864         parsed_command_line.HasSwitch(switches::kNoFirstRun)))
865      first_run_ui_bypass = true;
866  }
867
868  // TODO(viettrungluu): why don't we run this earlier?
869  if (!parsed_command_line.HasSwitch(switches::kNoErrorDialogs))
870    WarnAboutMinimumSystemRequirements();
871
872  InitializeNetworkOptions(parsed_command_line);
873
874  // Initialize histogram statistics gathering system.
875  StatisticsRecorder statistics;
876
877  // Initialize histogram synchronizer system. This is a singleton and is used
878  // for posting tasks via NewRunnableMethod. Its deleted when it goes out of
879  // scope. Even though NewRunnableMethod does AddRef and Release, the object
880  // will not be deleted after the Task is executed.
881  scoped_refptr<HistogramSynchronizer> histogram_synchronizer =
882      new HistogramSynchronizer();
883
884  // Initialize the prefs of the local state.
885  browser::RegisterLocalState(local_state);
886
887  // Now that all preferences have been registered, set the install date
888  // for the uninstall metrics if this is our first run. This only actually
889  // gets used if the user has metrics reporting enabled at uninstall time.
890  int64 install_date =
891      local_state->GetInt64(prefs::kUninstallMetricsInstallDate);
892  if (install_date == 0) {
893    local_state->SetInt64(prefs::kUninstallMetricsInstallDate,
894                          base::Time::Now().ToTimeT());
895  }
896
897#if defined(OS_MACOSX)
898  // Get the Keychain API to register for distributed notifications on the main
899  // thread, which has a proper CFRunloop, instead of later on the I/O thread,
900  // which doesn't. This ensures those notifications will get delivered
901  // properly. See issue 37766.
902  // (Note that the callback mask here is empty. I don't want to register for
903  // any callbacks, I just want to initialize the mechanism.)
904  SecKeychainAddCallback(&KeychainCallback, 0, NULL);
905#endif
906
907  CreateChildThreads(browser_process.get());
908
909#if defined(OS_CHROMEOS)
910  // Now that the file thread exists we can record our stats.
911  chromeos::BootTimesLoader::Get()->RecordChromeMainStats();
912#endif
913
914  // Record last shutdown time into a histogram.
915  browser_shutdown::ReadLastShutdownInfo();
916
917#if defined(OS_WIN)
918  // On Windows, we use our startup as an opportunity to do upgrade/uninstall
919  // tasks.  Those care whether the browser is already running.  On Linux/Mac,
920  // upgrade/uninstall happen separately.
921  bool already_running = Upgrade::IsBrowserAlreadyRunning();
922
923  // If the command line specifies 'uninstall' then we need to work here
924  // unless we detect another chrome browser running.
925  if (parsed_command_line.HasSwitch(switches::kUninstall))
926    return DoUninstallTasks(already_running);
927#endif
928
929  if (parsed_command_line.HasSwitch(switches::kHideIcons) ||
930      parsed_command_line.HasSwitch(switches::kShowIcons))
931    return HandleIconsCommands(parsed_command_line);
932  if (parsed_command_line.HasSwitch(switches::kMakeDefaultBrowser)) {
933    return ShellIntegration::SetAsDefaultBrowser() ?
934        ResultCodes::NORMAL_EXIT : ResultCodes::SHELL_INTEGRATION_FAILED;
935  }
936
937#if !defined(OS_MACOSX)
938  // In environments other than Mac OS X we support import of settings
939  // from other browsers. In case this process is a short-lived "import"
940  // process that another browser runs just to import the settings, we
941  // don't want to be checking for another browser process, by design.
942  if (!(parsed_command_line.HasSwitch(switches::kImport) ||
943        parsed_command_line.HasSwitch(switches::kImportFromFile))) {
944#endif
945    // When another process is running, use that process instead of starting a
946    // new one. NotifyOtherProcess will currently give the other process up to
947    // 20 seconds to respond. Note that this needs to be done before we attempt
948    // to read the profile.
949    switch (process_singleton.NotifyOtherProcessOrCreate()) {
950      case ProcessSingleton::PROCESS_NONE:
951        // No process already running, fall through to starting a new one.
952        break;
953
954      case ProcessSingleton::PROCESS_NOTIFIED:
955#if defined(OS_POSIX) && !defined(OS_MACOSX)
956        printf("%s\n", base::SysWideToNativeMB(
957                   l10n_util::GetString(IDS_USED_EXISTING_BROWSER)).c_str());
958#endif
959        return ResultCodes::NORMAL_EXIT;
960
961      case ProcessSingleton::PROFILE_IN_USE:
962        return ResultCodes::PROFILE_IN_USE;
963
964      case ProcessSingleton::LOCK_ERROR:
965        LOG(ERROR) << "Failed to create a ProcessSingleton for your profile "
966                      "directory. This means that running multiple instances "
967                      "would start multiple browser processes rather than "
968                      "opening a new window in the existing process. Aborting "
969                      "now to avoid profile corruption.";
970        return ResultCodes::PROFILE_IN_USE;
971
972      default:
973        NOTREACHED();
974    }
975#if !defined(OS_MACOSX)  // closing brace for if
976  }
977#endif
978
979  // Profile creation ----------------------------------------------------------
980
981#if defined(OS_CHROMEOS)
982  // Initialize the screen locker now so that it can receive
983  // LOGIN_USER_CHANGED notification from UserManager.
984  chromeos::ScreenLocker::InitClass();
985
986  // This forces the ProfileManager to be created and register for the
987  // notification it needs to track the logged in user.
988  g_browser_process->profile_manager()->GetDefaultProfile();
989
990  if (parsed_command_line.HasSwitch(switches::kLoginUser)) {
991    std::string username =
992        parsed_command_line.GetSwitchValueASCII(switches::kLoginUser);
993    LOG(INFO) << "Relaunching browser for user: " << username;
994    chromeos::UserManager::Get()->UserLoggedIn(username);
995  }
996#endif
997
998  Profile* profile = CreateProfile(parameters, user_data_dir);
999  if (!profile)
1000    return ResultCodes::NORMAL_EXIT;
1001
1002  // Post-profile init ---------------------------------------------------------
1003
1004  PrefService* user_prefs = profile->GetPrefs();
1005  DCHECK(user_prefs);
1006
1007  // Tests should be able to tune login manager before showing it.
1008  // Thus only show login manager in normal (non-testing) mode.
1009  if (!parameters.ui_task) {
1010    OptionallyRunChromeOSLoginManager(parsed_command_line);
1011  }
1012
1013#if !defined(OS_MACOSX)
1014  // Importing other browser settings is done in a browser-like process
1015  // that exits when this task has finished.
1016  // TODO(port):  Port to Mac
1017  if (parsed_command_line.HasSwitch(switches::kImport) ||
1018      parsed_command_line.HasSwitch(switches::kImportFromFile)) {
1019    return FirstRun::ImportNow(profile, parsed_command_line);
1020  }
1021#endif
1022
1023#if defined(OS_WIN)
1024  // Do the tasks if chrome has been upgraded while it was last running.
1025  if (!already_running && Upgrade::DoUpgradeTasks(parsed_command_line))
1026    return ResultCodes::NORMAL_EXIT;
1027#endif
1028
1029  // Check if there is any machine level Chrome installed on the current
1030  // machine. If yes and the current Chrome process is user level, we do not
1031  // allow the user level Chrome to run. So we notify the user and uninstall
1032  // user level Chrome.
1033  // Note this check should only happen here, after all the checks above
1034  // (uninstall, resource bundle initialization, other chrome browser
1035  // processes etc).
1036  if (CheckMachineLevelInstall())
1037    return ResultCodes::MACHINE_LEVEL_INSTALL_EXISTS;
1038
1039  // Create the TranslateManager singleton.
1040  Singleton<TranslateManager>::get();
1041
1042#if defined(OS_MACOSX)
1043  if (!parsed_command_line.HasSwitch(switches::kNoFirstRun)) {
1044    // Disk image installation is sort of a first-run task, so it shares the
1045    // kNoFirstRun switch.
1046    if (MaybeInstallFromDiskImage()) {
1047      // The application was installed and the installed copy has been
1048      // launched.  This process is now obsolete.  Exit.
1049      return ResultCodes::NORMAL_EXIT;
1050    }
1051  }
1052#endif
1053
1054  // Show the First Run UI if this is the first time Chrome has been run on
1055  // this computer, or we're being compelled to do so by a command line flag.
1056  // Note that this be done _after_ the PrefService is initialized and all
1057  // preferences are registered, since some of the code that the importer
1058  // touches reads preferences.
1059  if (is_first_run) {
1060    if (!first_run_ui_bypass) {
1061#if defined(OS_WIN)
1062      FirstRun::AutoImport(profile,
1063                           master_prefs.homepage_defined,
1064                           master_prefs.do_import_items,
1065                           master_prefs.dont_import_items,
1066                           master_prefs.run_search_engine_experiment,
1067                           master_prefs.randomize_search_engine_experiment,
1068                           &process_singleton);
1069#else
1070      if (!OpenFirstRunDialog(profile,
1071                              master_prefs.homepage_defined,
1072                              master_prefs.do_import_items,
1073                              master_prefs.dont_import_items,
1074                              master_prefs.run_search_engine_experiment,
1075                              master_prefs.randomize_search_engine_experiment,
1076                              &process_singleton)) {
1077        return ResultCodes::NORMAL_EXIT;
1078      }
1079#endif
1080#if defined(OS_POSIX)
1081      // On Windows, the download is tagged with enable/disable stats so there
1082      // is no need for this code.
1083
1084      // If stats reporting was turned on by the first run dialog then toggle
1085      // the pref.
1086      if (GoogleUpdateSettings::GetCollectStatsConsent())
1087        local_state->SetBoolean(prefs::kMetricsReportingEnabled, true);
1088#endif  // OS_POSIX
1089    }  // if (!first_run_ui_bypass)
1090
1091    Browser::SetNewHomePagePrefs(user_prefs);
1092  }
1093
1094  // Sets things up so that if we crash from this point on, a dialog will
1095  // popup asking the user to restart chrome. It is done this late to avoid
1096  // testing against a bunch of special cases that are taken care early on.
1097  PrepareRestartOnCrashEnviroment(parsed_command_line);
1098
1099  // Initialize and maintain network predictor module, which handles DNS
1100  // pre-resolution, as well as TCP/IP connection pre-warming.
1101  // This also registers an observer to discard data when closing incognito
1102  // mode.
1103  chrome_browser_net::PredictorInit dns_prefetch(
1104      user_prefs,
1105      local_state,
1106      parsed_command_line.HasSwitch(switches::kEnablePreconnect),
1107      parsed_command_line.HasSwitch(switches::kPreconnectDespiteProxy));
1108
1109#if defined(OS_WIN)
1110  win_util::ScopedCOMInitializer com_initializer;
1111
1112  // Init the RLZ library. This just binds the dll and schedules a task on the
1113  // file thread to be run sometime later. If this is the first run we record
1114  // the installation event.
1115  RLZTracker::InitRlzDelayed(is_first_run, master_prefs.ping_delay);
1116#endif
1117
1118  // Configure the network module so it has access to resources.
1119  net::NetModule::SetResourceProvider(chrome_common_net::NetResourceProvider);
1120
1121  // Register our global network handler for chrome:// and
1122  // chrome-extension:// URLs.
1123  RegisterURLRequestChromeJob();
1124  RegisterExtensionProtocols();
1125  RegisterMetadataURLRequestHandler();
1126
1127  // If path to partner services customization document was passed on command
1128  // line, apply the customizations (Chrome OS only).
1129  // TODO(denisromanov): Remove this when not needed for testing.
1130  OptionallyApplyServicesCustomizationFromCommandLine(parsed_command_line,
1131                                                      &browser_init);
1132
1133  // In unittest mode, this will do nothing.  In normal mode, this will create
1134  // the global GoogleURLTracker and IntranetRedirectDetector instances, which
1135  // will promptly go to sleep for five and seven seconds, respectively (to
1136  // avoid slowing startup), and wake up afterwards to see if they should do
1137  // anything else.
1138  //
1139  // A simpler way of doing all this would be to have some function which could
1140  // give the time elapsed since startup, and simply have these objects check
1141  // that when asked to initialize themselves, but this doesn't seem to exist.
1142  //
1143  // These can't be created in the BrowserProcessImpl constructor because they
1144  // need to read prefs that get set after that runs.
1145  browser_process->google_url_tracker();
1146  browser_process->intranet_redirect_detector();
1147
1148  // Do initialize the plug-in service (and related preferences).
1149  PluginService::InitGlobalInstance(profile);
1150
1151  // Prepare for memory caching of SDCH dictionaries.
1152  // Perform A/B test to measure global impact of SDCH support.
1153  // Set up a field trial to see what disabling SDCH does to latency of page
1154  // layout globally.
1155  FieldTrial::Probability kSDCH_DIVISOR = 1000;
1156  FieldTrial::Probability kSDCH_DISABLE_PROBABILITY = 1;  // 0.1% probability.
1157  scoped_refptr<FieldTrial> sdch_trial =
1158      new FieldTrial("GlobalSdch", kSDCH_DIVISOR);
1159
1160  // Use default of "" so that all domains are supported.
1161  std::string sdch_supported_domain("");
1162  if (parsed_command_line.HasSwitch(switches::kSdchFilter)) {
1163    sdch_supported_domain =
1164        parsed_command_line.GetSwitchValueASCII(switches::kSdchFilter);
1165  } else {
1166    sdch_trial->AppendGroup("_global_disable_sdch",
1167                            kSDCH_DISABLE_PROBABILITY);
1168    int sdch_enabled = sdch_trial->AppendGroup("_global_enable_sdch",
1169        FieldTrial::kAllRemainingProbability);
1170    if (sdch_enabled != sdch_trial->group())
1171      sdch_supported_domain = "never_enabled_sdch_for_any_domain";
1172  }
1173
1174  SdchManager sdch_manager;  // Singleton database.
1175  sdch_manager.set_sdch_fetcher(new SdchDictionaryFetcher);
1176  sdch_manager.EnableSdchSupport(sdch_supported_domain);
1177
1178  MetricsService* metrics = InitializeMetrics(parsed_command_line, local_state);
1179  InstallJankometer(parsed_command_line);
1180
1181#if defined(OS_WIN) && !defined(GOOGLE_CHROME_BUILD)
1182  if (parsed_command_line.HasSwitch(switches::kDebugPrint)) {
1183    printing::PrintedDocument::set_debug_dump_path(
1184        parsed_command_line.GetSwitchValue(switches::kDebugPrint));
1185  }
1186#endif
1187
1188  HandleTestParameters(parsed_command_line);
1189  RecordBreakpadStatusUMA(metrics);
1190
1191  // Stat the directory with the inspector's files so that we can know if we
1192  // should display the entry in the context menu or not.
1193  browser_process->CheckForInspectorFiles();
1194
1195#if defined(OS_CHROMEOS)
1196  metrics->StartExternalMetrics();
1197#endif
1198
1199  if (profile->GetExtensionsService()) {
1200    // This will initialize bookmarks. Call it after bookmark import is done.
1201    // See issue 40144.
1202    profile->GetExtensionsService()->InitEventRouters();
1203  }
1204
1205#if defined(OS_WIN)
1206  // We check this here because if the profile is OTR (chromeos possibility)
1207  // it won't still be accessible after browser is destroyed.
1208  bool record_search_engine = is_first_run && !profile->IsOffTheRecord();
1209#endif
1210
1211    // ChildProcess:: is a misnomer unless you consider context.  Use
1212    // of --wait-for-debugger only makes sense when Chrome itself is a
1213    // child process (e.g. when launched by PyAuto).
1214  if (parsed_command_line.HasSwitch(switches::kWaitForDebugger)) {
1215    ChildProcess::WaitForDebugger(L"Browser");
1216  }
1217
1218  int result_code = ResultCodes::NORMAL_EXIT;
1219  if (parameters.ui_task) {
1220    // We are in test mode. Run one task and enter the main message loop.
1221    if (pool)
1222      pool->Recycle();
1223    parameters.ui_task->Run();
1224    delete parameters.ui_task;
1225  } else {
1226    // We are in regular browser boot sequence. Open initial stabs and enter
1227    // the main message loop.
1228    if (browser_init.Start(parsed_command_line, std::wstring(), profile,
1229                           &result_code)) {
1230#if (defined(OS_WIN) || defined(OS_LINUX)) && !defined(OS_CHROMEOS)
1231      // Initialize autoupdate timer. Timer callback costs basically nothing
1232      // when browser is not in persistent mode, so it's OK to let it ride on
1233      // the main thread. This needs to be done here because we don't want
1234      // to start the timer when Chrome is run inside a test harness.
1235      g_browser_process->StartAutoupdateTimer();
1236#endif
1237
1238#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
1239      // On Linux, the running exe will be updated if an upgrade becomes
1240      // available while the browser is running.  We need to save the last
1241      // modified time of the exe, so we can compare to determine if there is
1242      // an upgrade while the browser is kept alive by a persistent extension.
1243      Upgrade::SaveLastModifiedTimeOfExe();
1244#endif
1245
1246      // Record now as the last successful chrome start.
1247      GoogleUpdateSettings::SetLastRunTime();
1248      // Call Recycle() here as late as possible, before going into the loop
1249      // because Start() will add things to it while creating the main window.
1250      if (pool)
1251        pool->Recycle();
1252      RunUIMessageLoop(browser_process.get());
1253    }
1254  }
1255
1256#if defined(OS_WIN)
1257  // If it's the first run, log the search engine chosen.  We wait until
1258  // shutdown because otherwise we can't be sure the user has finished
1259  // selecting a search engine through the dialog reached from the first run
1260  // bubble link.
1261  if (FirstRun::InSearchExperimentLocale() && record_search_engine) {
1262    const TemplateURL* default_search_engine =
1263        profile->GetTemplateURLModel()->GetDefaultSearchProvider();
1264    // Record the search engine chosen.
1265    if (master_prefs.run_search_engine_experiment) {
1266      UMA_HISTOGRAM_ENUMERATION(
1267          "Chrome.SearchSelectExperiment",
1268          TemplateURLPrepopulateData::GetSearchEngineType(
1269          default_search_engine),
1270          TemplateURLPrepopulateData::SEARCH_ENGINE_MAX);
1271      // If the selection has been randomized, also record the winner by slot.
1272      if (master_prefs.randomize_search_engine_experiment) {
1273        size_t engine_pos = profile->GetTemplateURLModel()->
1274            GetSearchEngineDialogSlot();
1275        if (engine_pos < 4) {
1276          std::string experiment_type = "Chrome.SearchSelectExperimentSlot";
1277          // Nicer in UMA if slots are 1-based.
1278          experiment_type.push_back('1' + engine_pos);
1279          UMA_HISTOGRAM_ENUMERATION(
1280              experiment_type,
1281              TemplateURLPrepopulateData::GetSearchEngineType(
1282              default_search_engine),
1283              TemplateURLPrepopulateData::SEARCH_ENGINE_MAX);
1284        } else {
1285          NOTREACHED() << "Invalid search engine selection slot.";
1286        }
1287      }
1288    } else {
1289      UMA_HISTOGRAM_ENUMERATION(
1290          "Chrome.SearchSelectExempt",
1291          TemplateURLPrepopulateData::GetSearchEngineType(
1292              default_search_engine),
1293          TemplateURLPrepopulateData::SEARCH_ENGINE_MAX);
1294    }
1295  }
1296#endif
1297
1298  chrome_browser_net_websocket_experiment::WebSocketExperimentRunner::Stop();
1299
1300  process_singleton.Cleanup();
1301
1302  metrics->Stop();
1303
1304  // browser_shutdown takes care of deleting browser_process, so we need to
1305  // release it.
1306  ignore_result(browser_process.release());
1307  browser_shutdown::Shutdown();
1308
1309  return result_code;
1310}
1311