first_run.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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/first_run/first_run.h"
6
7#include "base/command_line.h"
8#include "base/compiler_specific.h"
9#include "base/file_util.h"
10#include "base/metrics/histogram.h"
11#include "base/path_service.h"
12#include "base/stringprintf.h"
13#include "base/utf_string_conversions.h"
14#include "build/build_config.h"
15#include "chrome/browser/browser_process.h"
16#include "chrome/browser/first_run/first_run_dialog.h"
17#include "chrome/browser/first_run/first_run_import_observer.h"
18#include "chrome/browser/first_run/first_run_internal.h"
19#include "chrome/browser/google/google_util.h"
20#include "chrome/browser/importer/external_process_importer_host.h"
21#include "chrome/browser/importer/importer_host.h"
22#include "chrome/browser/importer/importer_list.h"
23#include "chrome/browser/importer/importer_progress_dialog.h"
24#include "chrome/browser/importer/importer_progress_observer.h"
25#include "chrome/browser/prefs/pref_service.h"
26#include "chrome/browser/process_singleton.h"
27#include "chrome/browser/profiles/profile_manager.h"
28#include "chrome/browser/search_engines/template_url_service.h"
29#include "chrome/browser/search_engines/template_url_service_factory.h"
30#include "chrome/browser/shell_integration.h"
31#include "chrome/browser/ui/browser.h"
32#include "chrome/browser/ui/browser_finder.h"
33#include "chrome/browser/ui/browser_tabstrip.h"
34#include "chrome/browser/ui/global_error/global_error_service.h"
35#include "chrome/browser/ui/global_error/global_error_service_factory.h"
36#include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
37#include "chrome/common/chrome_paths.h"
38#include "chrome/common/chrome_switches.h"
39#include "chrome/common/pref_names.h"
40#include "chrome/common/startup_metric_utils.h"
41#include "chrome/common/url_constants.h"
42#include "chrome/installer/util/master_preferences.h"
43#include "chrome/installer/util/master_preferences_constants.h"
44#include "chrome/installer/util/util_constants.h"
45#include "content/public/browser/notification_service.h"
46#include "content/public/browser/notification_types.h"
47#include "content/public/browser/user_metrics.h"
48#include "content/public/browser/web_contents.h"
49#include "googleurl/src/gurl.h"
50
51using content::UserMetricsAction;
52
53namespace {
54
55FilePath GetDefaultPrefFilePath(bool create_profile_dir,
56                                const FilePath& user_data_dir) {
57  FilePath default_pref_dir =
58      ProfileManager::GetDefaultProfileDir(user_data_dir);
59  if (create_profile_dir) {
60    if (!file_util::PathExists(default_pref_dir)) {
61      if (!file_util::CreateDirectory(default_pref_dir))
62        return FilePath();
63    }
64  }
65  return ProfileManager::GetProfilePrefsPath(default_pref_dir);
66}
67
68// Sets the |items| bitfield according to whether the import data specified by
69// |import_type| should be be auto imported or not.
70void SetImportItem(PrefService* user_prefs,
71                   const char* pref_path,
72                   int import_items,
73                   int dont_import_items,
74                   importer::ImportItem import_type,
75                   int& items) {
76  // Work out whether an item is to be imported according to what is specified
77  // in master preferences.
78  bool should_import = false;
79  bool master_pref_set =
80      ((import_items | dont_import_items) & import_type) != 0;
81  bool master_pref = ((import_items & ~dont_import_items) & import_type) != 0;
82
83  if (import_type == importer::HISTORY ||
84      ((import_type != importer::FAVORITES) &&
85      first_run::internal::IsOrganicFirstRun())) {
86    // History is always imported unless turned off in master_preferences.
87    // Search engines are only imported in certain builds unless overridden
88    // in master_preferences.Home page is imported in organic builds only unless
89    // turned off in master_preferences.
90    should_import = !master_pref_set || master_pref;
91  } else {
92    // Bookmarks are never imported, unless turned on in master_preferences.
93    // Search engine and home page import behaviour is similar in non organic
94    // builds.
95    should_import = master_pref_set && master_pref;
96  }
97
98  // If an import policy is set, import items according to policy. If no master
99  // preference is set, but a corresponding recommended policy is set, import
100  // item according to recommended policy. If both a master preference and a
101  // recommended policy is set, the master preference wins. If neither
102  // recommended nor managed policies are set, import item according to what we
103  // worked out above.
104  if (master_pref_set)
105    user_prefs->SetBoolean(pref_path, should_import);
106
107  if (!user_prefs->FindPreference(pref_path)->IsDefaultValue()) {
108    if (user_prefs->GetBoolean(pref_path))
109      items |= import_type;
110  } else { // no policy (recommended or managed) is set
111    if (should_import)
112      items |= import_type;
113  }
114
115  user_prefs->ClearPref(pref_path);
116}
117
118// Imports bookmarks from an html file. The path to the file is provided in
119// the command line.
120int ImportFromFile(Profile* profile, const CommandLine& cmdline) {
121  FilePath file_path = cmdline.GetSwitchValuePath(switches::kImportFromFile);
122  if (file_path.empty()) {
123    NOTREACHED();
124    return false;
125  }
126  scoped_refptr<ImporterHost> importer_host(new ImporterHost);
127  importer_host->set_headless();
128
129  importer::SourceProfile source_profile;
130  source_profile.importer_type = importer::TYPE_BOOKMARKS_FILE;
131  source_profile.source_path = file_path;
132
133  FirstRunImportObserver importer_observer;
134  importer::ShowImportProgressDialog(importer::FAVORITES,
135                                     importer_host,
136                                     &importer_observer,
137                                     source_profile,
138                                     profile,
139                                     true);
140
141  importer_observer.RunLoop();
142  return importer_observer.import_result();
143}
144
145}  // namespace
146
147namespace first_run {
148namespace internal {
149
150FirstRunState first_run_ = FIRST_RUN_UNKNOWN;
151
152installer::MasterPreferences* LoadMasterPrefs(FilePath* master_prefs_path)
153{
154  *master_prefs_path = FilePath(MasterPrefsPath());
155  if (master_prefs_path->empty())
156    return NULL;
157  installer::MasterPreferences* install_prefs =
158      new installer::MasterPreferences(*master_prefs_path);
159  if (!install_prefs->read_from_file()) {
160    delete install_prefs;
161    return NULL;
162  }
163
164  return install_prefs;
165}
166
167bool CopyPrefFile(const FilePath& user_data_dir,
168                  const FilePath& master_prefs_path) {
169  FilePath user_prefs = GetDefaultPrefFilePath(true, user_data_dir);
170  if (user_prefs.empty())
171    return false;
172
173  // The master prefs are regular prefs so we can just copy the file
174  // to the default place and they just work.
175  return file_util::CopyFile(master_prefs_path, user_prefs);
176}
177
178void SetupMasterPrefsFromInstallPrefs(
179    MasterPrefs* out_prefs,
180    installer::MasterPreferences* install_prefs) {
181  bool value = false;
182  if (install_prefs->GetBool(
183          installer::master_preferences::kDistroImportSearchPref, &value)) {
184    if (value) {
185      out_prefs->do_import_items |= importer::SEARCH_ENGINES;
186    } else {
187      out_prefs->dont_import_items |= importer::SEARCH_ENGINES;
188    }
189  }
190
191  // If we're suppressing the first-run bubble, set that preference now.
192  // Otherwise, wait until the user has completed first run to set it, so the
193  // user is guaranteed to see the bubble iff he or she has completed the first
194  // run process.
195  if (install_prefs->GetBool(
196          installer::master_preferences::kDistroSuppressFirstRunBubble,
197          &value) && value)
198    SetShowFirstRunBubblePref(false);
199
200  if (install_prefs->GetBool(
201          installer::master_preferences::kDistroImportHistoryPref,
202          &value)) {
203    if (value) {
204      out_prefs->do_import_items |= importer::HISTORY;
205    } else {
206      out_prefs->dont_import_items |= importer::HISTORY;
207    }
208  }
209
210  std::string not_used;
211  out_prefs->homepage_defined = install_prefs->GetString(
212      prefs::kHomePage, &not_used);
213
214  if (install_prefs->GetBool(
215          installer::master_preferences::kDistroImportHomePagePref,
216          &value)) {
217    if (value) {
218      out_prefs->do_import_items |= importer::HOME_PAGE;
219    } else {
220      out_prefs->dont_import_items |= importer::HOME_PAGE;
221    }
222  }
223
224  // Bookmarks are never imported unless specifically turned on.
225  if (install_prefs->GetBool(
226          installer::master_preferences::kDistroImportBookmarksPref,
227          &value)) {
228    if (value)
229      out_prefs->do_import_items |= importer::FAVORITES;
230    else
231      out_prefs->dont_import_items |= importer::FAVORITES;
232  }
233
234  if (install_prefs->GetBool(
235          installer::master_preferences::kMakeChromeDefaultForUser,
236          &value) && value) {
237    out_prefs->make_chrome_default = true;
238  }
239
240  if (install_prefs->GetBool(
241          installer::master_preferences::kSuppressFirstRunDefaultBrowserPrompt,
242          &value) && value) {
243    out_prefs->suppress_first_run_default_browser_prompt = true;
244  }
245}
246
247void SetDefaultBrowser(installer::MasterPreferences* install_prefs){
248  // Even on the first run we only allow for the user choice to take effect if
249  // no policy has been set by the admin.
250  if (!g_browser_process->local_state()->IsManagedPreference(
251          prefs::kDefaultBrowserSettingEnabled)) {
252    bool value = false;
253    if (install_prefs->GetBool(
254            installer::master_preferences::kMakeChromeDefaultForUser,
255            &value) && value) {
256      ShellIntegration::SetAsDefaultBrowser();
257    }
258  } else {
259    if (g_browser_process->local_state()->GetBoolean(
260            prefs::kDefaultBrowserSettingEnabled)) {
261      ShellIntegration::SetAsDefaultBrowser();
262    }
263  }
264}
265
266void SetShowWelcomePagePrefIfNeeded(
267    installer::MasterPreferences* install_prefs) {
268  bool value = false;
269  if (install_prefs->GetBool(
270          installer::master_preferences::kDistroShowWelcomePage, &value)
271          && value) {
272    SetShowWelcomePagePref();
273  }
274}
275
276bool SkipFirstRunUI(installer::MasterPreferences* install_prefs) {
277  bool value = false;
278  install_prefs->GetBool(installer::master_preferences::kDistroSkipFirstRunPref,
279                         &value);
280  return value;
281}
282
283void SetRLZPref(first_run::MasterPrefs* out_prefs,
284                installer::MasterPreferences* install_prefs) {
285  if (!install_prefs->GetInt(installer::master_preferences::kDistroPingDelay,
286                    &out_prefs->ping_delay)) {
287    // Default value in case master preferences is missing or corrupt,
288    // or ping_delay is missing.
289    out_prefs->ping_delay = 90;
290  }
291}
292
293// -- Platform-specific functions --
294
295#if !defined(OS_LINUX) && !defined(OS_BSD)
296bool IsOrganicFirstRun() {
297  std::string brand;
298  google_util::GetBrand(&brand);
299  return google_util::IsOrganicFirstRun(brand);
300}
301#endif
302
303#if !defined(USE_AURA)
304void AutoImportPlatformCommon(
305    scoped_refptr<ImporterHost> importer_host,
306    Profile* profile,
307    bool homepage_defined,
308    int import_items,
309    int dont_import_items,
310    bool make_chrome_default) {
311  FilePath local_state_path;
312  PathService::Get(chrome::FILE_LOCAL_STATE, &local_state_path);
313  bool local_state_file_exists = file_util::PathExists(local_state_path);
314
315  scoped_refptr<ImporterList> importer_list(new ImporterList(NULL));
316  importer_list->DetectSourceProfilesHack();
317
318  // Do import if there is an available profile for us to import.
319  if (importer_list->count() > 0) {
320    // Don't show the warning dialog if import fails.
321    importer_host->set_headless();
322    int items = 0;
323
324    if (IsOrganicFirstRun()) {
325      // Home page is imported in organic builds only unless turned off or
326      // defined in master_preferences.
327      if (homepage_defined) {
328        dont_import_items |= importer::HOME_PAGE;
329        if (import_items & importer::HOME_PAGE)
330          import_items &= ~importer::HOME_PAGE;
331      }
332      // Search engines are not imported automatically in organic builds if the
333      // user already has a user preferences directory.
334      if (local_state_file_exists) {
335        dont_import_items |= importer::SEARCH_ENGINES;
336        if (import_items & importer::SEARCH_ENGINES)
337          import_items &= ~importer::SEARCH_ENGINES;
338      }
339    }
340
341    PrefService* user_prefs = profile->GetPrefs();
342
343    SetImportItem(user_prefs,
344                  prefs::kImportHistory,
345                  import_items,
346                  dont_import_items,
347                  importer::HISTORY,
348                  items);
349    SetImportItem(user_prefs,
350                  prefs::kImportHomepage,
351                  import_items,
352                  dont_import_items,
353                  importer::HOME_PAGE,
354                  items);
355    SetImportItem(user_prefs,
356                  prefs::kImportSearchEngine,
357                  import_items,
358                  dont_import_items,
359                  importer::SEARCH_ENGINES,
360                  items);
361    SetImportItem(user_prefs,
362                  prefs::kImportBookmarks,
363                  import_items,
364                  dont_import_items,
365                  importer::FAVORITES,
366                  items);
367
368    ImportSettings(profile, importer_host, importer_list, items);
369  }
370
371  content::RecordAction(UserMetricsAction("FirstRunDef_Accept"));
372
373  // Launch the first run dialog only for certain builds, and only if the user
374  // has not already set preferences.
375  if (IsOrganicFirstRun() && !local_state_file_exists) {
376    startup_metric_utils::SetNonBrowserUIDisplayed();
377    ShowFirstRunDialog(profile);
378  }
379
380  if (make_chrome_default &&
381      ShellIntegration::CanSetAsDefaultBrowser() ==
382          ShellIntegration::SET_DEFAULT_UNATTENDED) {
383    ShellIntegration::SetAsDefaultBrowser();
384  }
385
386  // Display the first run bubble if there is a default search provider.
387  TemplateURLService* template_url =
388      TemplateURLServiceFactory::GetForProfile(profile);
389  if (template_url && template_url->GetDefaultSearchProvider())
390    FirstRunBubbleLauncher::ShowFirstRunBubbleSoon();
391  SetShowWelcomePagePref();
392  SetPersonalDataManagerFirstRunPref();
393}
394#endif  // !defined(USE_AURA)
395
396int ImportBookmarkFromFileIfNeeded(Profile* profile,
397                                   const CommandLine& cmdline) {
398  int return_code = true;
399  if (cmdline.HasSwitch(switches::kImportFromFile)) {
400    // Silently import preset bookmarks from file.
401    // This is an OEM scenario.
402    return_code = ImportFromFile(profile, cmdline);
403  }
404  return return_code;
405}
406
407}  // namespace internal
408}  // namespace first_run
409
410namespace first_run {
411
412MasterPrefs::MasterPrefs()
413    : ping_delay(0),
414      homepage_defined(false),
415      do_import_items(0),
416      dont_import_items(0),
417      make_chrome_default(false),
418      suppress_first_run_default_browser_prompt(false) {
419}
420
421MasterPrefs::~MasterPrefs() {}
422
423bool IsChromeFirstRun() {
424  if (internal::first_run_ != internal::FIRST_RUN_UNKNOWN)
425    return internal::first_run_ == internal::FIRST_RUN_TRUE;
426
427  FilePath first_run_sentinel;
428  if (!internal::GetFirstRunSentinelFilePath(&first_run_sentinel) ||
429      file_util::PathExists(first_run_sentinel)) {
430    internal::first_run_ = internal::FIRST_RUN_FALSE;
431    return false;
432  }
433  internal::first_run_ = internal::FIRST_RUN_TRUE;
434  return true;
435}
436
437bool CreateSentinel() {
438  FilePath first_run_sentinel;
439  if (!internal::GetFirstRunSentinelFilePath(&first_run_sentinel))
440    return false;
441  return file_util::WriteFile(first_run_sentinel, "", 0) != -1;
442}
443
444std::string GetPingDelayPrefName() {
445  return base::StringPrintf("%s.%s",
446                            installer::master_preferences::kDistroDict,
447                            installer::master_preferences::kDistroPingDelay);
448}
449
450void RegisterUserPrefs(PrefService* prefs) {
451  prefs->RegisterIntegerPref(GetPingDelayPrefName().c_str(),
452                             0,
453                             PrefService::UNSYNCABLE_PREF);
454}
455
456bool RemoveSentinel() {
457  FilePath first_run_sentinel;
458  if (!internal::GetFirstRunSentinelFilePath(&first_run_sentinel))
459    return false;
460  return file_util::Delete(first_run_sentinel, false);
461}
462
463bool SetShowFirstRunBubblePref(bool show_bubble) {
464  PrefService* local_state = g_browser_process->local_state();
465  if (!local_state)
466    return false;
467  local_state->SetBoolean(prefs::kShouldShowFirstRunBubble, show_bubble);
468  return true;
469}
470
471bool SetShowWelcomePagePref() {
472  PrefService* local_state = g_browser_process->local_state();
473  if (!local_state)
474    return false;
475  if (!local_state->FindPreference(prefs::kShouldShowWelcomePage)) {
476    local_state->RegisterBooleanPref(prefs::kShouldShowWelcomePage, false);
477    local_state->SetBoolean(prefs::kShouldShowWelcomePage, true);
478  }
479  return true;
480}
481
482bool SetPersonalDataManagerFirstRunPref() {
483  PrefService* local_state = g_browser_process->local_state();
484  if (!local_state)
485    return false;
486  if (!local_state->FindPreference(
487          prefs::kAutofillPersonalDataManagerFirstRun)) {
488    local_state->RegisterBooleanPref(
489        prefs::kAutofillPersonalDataManagerFirstRun, false);
490    local_state->SetBoolean(prefs::kAutofillPersonalDataManagerFirstRun, true);
491  }
492  return true;
493}
494
495void LogFirstRunMetric(FirstRunBubbleMetric metric) {
496  UMA_HISTOGRAM_ENUMERATION("FirstRun.SearchEngineBubble", metric,
497                            NUM_FIRST_RUN_BUBBLE_METRICS);
498}
499
500// static
501void FirstRunBubbleLauncher::ShowFirstRunBubbleSoon() {
502  SetShowFirstRunBubblePref(true);
503  // This FirstRunBubbleLauncher instance will manage its own lifetime.
504  new FirstRunBubbleLauncher();
505}
506
507FirstRunBubbleLauncher::FirstRunBubbleLauncher() {
508  registrar_.Add(this, content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
509                 content::NotificationService::AllSources());
510}
511
512FirstRunBubbleLauncher::~FirstRunBubbleLauncher() {}
513
514void FirstRunBubbleLauncher::Observe(
515    int type,
516    const content::NotificationSource& source,
517    const content::NotificationDetails& details) {
518  DCHECK_EQ(type, content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME);
519  Browser* browser = browser::FindBrowserWithWebContents(
520      content::Source<content::WebContents>(source).ptr());
521  if (!browser || !browser->is_type_tabbed())
522    return;
523
524  // Check the preference to determine if the bubble should be shown.
525  PrefService* prefs = g_browser_process->local_state();
526  if (!prefs || !prefs->GetBoolean(prefs::kShouldShowFirstRunBubble)) {
527    delete this;
528    return;
529  }
530
531  content::WebContents* contents = chrome::GetActiveWebContents(browser);
532  if (contents && contents->GetURL().SchemeIs(chrome::kChromeUIScheme)) {
533    // Suppress the first run bubble if the sync promo is showing.
534    if (contents->GetURL().host() == chrome::kChromeUISyncPromoHost)
535      return;
536
537    // Suppress the first run bubble if 'make chrome metro' flow is showing.
538    if (contents->GetURL().host() == chrome::kChromeUIMetroFlowHost)
539      return;
540
541    // Suppress the first run bubble if the NTP sync promo bubble is showing.
542    if (contents->GetURL().host() == chrome::kChromeUINewTabHost) {
543      NewTabUI* new_tab_ui =
544          NewTabUI::FromWebUIController(contents->GetWebUI()->GetController());
545      if (new_tab_ui && new_tab_ui->showing_sync_bubble())
546        return;
547    }
548  }
549
550  // Suppress the first run bubble if a global error bubble is pending.
551  GlobalErrorService* global_error_service =
552      GlobalErrorServiceFactory::GetForProfile(browser->profile());
553  if (global_error_service->GetFirstGlobalErrorWithBubbleView() != NULL)
554    return;
555
556  // Reset the preference and notifications to avoid showing the bubble again.
557  prefs->SetBoolean(prefs::kShouldShowFirstRunBubble, false);
558
559  // Show the bubble now and destroy this bubble launcher.
560  browser->ShowFirstRunBubble();
561  delete this;
562}
563
564}  // namespace first_run
565