Home | History | Annotate | Download | only in first_run
      1 // Copyright (c) 2011 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/path_service.h"
     11 #include "base/utf_string_conversions.h"
     12 #include "build/build_config.h"
     13 #include "chrome/browser/browser_process.h"
     14 #include "chrome/browser/first_run/first_run_dialog.h"
     15 #include "chrome/browser/first_run/first_run_import_observer.h"
     16 #include "chrome/browser/importer/external_process_importer_host.h"
     17 #include "chrome/browser/importer/importer_host.h"
     18 #include "chrome/browser/importer/importer_list.h"
     19 #include "chrome/browser/importer/importer_progress_dialog.h"
     20 #include "chrome/browser/importer/importer_progress_observer.h"
     21 #include "chrome/browser/metrics/user_metrics.h"
     22 #include "chrome/browser/prefs/pref_service.h"
     23 #include "chrome/browser/process_singleton.h"
     24 #include "chrome/browser/profiles/profile_manager.h"
     25 #include "chrome/browser/search_engines/template_url_model.h"
     26 #include "chrome/browser/shell_integration.h"
     27 #include "chrome/common/chrome_paths.h"
     28 #include "chrome/common/chrome_switches.h"
     29 #include "chrome/common/pref_names.h"
     30 #include "chrome/installer/util/master_preferences.h"
     31 #include "chrome/installer/util/master_preferences_constants.h"
     32 #include "chrome/installer/util/util_constants.h"
     33 #include "googleurl/src/gurl.h"
     34 
     35 #if defined(OS_WIN)
     36 // TODO(port): move more code in back from the first_run_win.cc module.
     37 #include "chrome/installer/util/google_update_settings.h"
     38 #include "chrome/installer/util/install_util.h"
     39 #endif
     40 
     41 namespace {
     42 
     43 // The kSentinelFile file absence will tell us it is a first run.
     44 const char kSentinelFile[] = "First Run";
     45 
     46 FilePath GetDefaultPrefFilePath(bool create_profile_dir,
     47                                 const FilePath& user_data_dir) {
     48   FilePath default_pref_dir =
     49       ProfileManager::GetDefaultProfileDir(user_data_dir);
     50   if (create_profile_dir) {
     51     if (!file_util::PathExists(default_pref_dir)) {
     52       if (!file_util::CreateDirectory(default_pref_dir))
     53         return FilePath();
     54     }
     55   }
     56   return ProfileManager::GetProfilePrefsPath(default_pref_dir);
     57 }
     58 
     59 }  // namespace
     60 
     61 // FirstRun -------------------------------------------------------------------
     62 
     63 FirstRun::FirstRunState FirstRun::first_run_ = FIRST_RUN_UNKNOWN;
     64 
     65 FirstRun::MasterPrefs::MasterPrefs()
     66     : ping_delay(0),
     67       homepage_defined(false),
     68       do_import_items(0),
     69       dont_import_items(0),
     70       run_search_engine_experiment(false),
     71       randomize_search_engine_experiment(false),
     72       make_chrome_default(false) {
     73 }
     74 
     75 FirstRun::MasterPrefs::~MasterPrefs() {}
     76 
     77 // TODO(port): Import switches need to be ported to both Mac and Linux. Not all
     78 // import switches here are implemented for Linux. None are implemented for Mac
     79 // (as this function will not be called on Mac).
     80 int FirstRun::ImportNow(Profile* profile, const CommandLine& cmdline) {
     81   int return_code = true;
     82   if (cmdline.HasSwitch(switches::kImportFromFile)) {
     83     // Silently import preset bookmarks from file.
     84     // This is an OEM scenario.
     85     return_code = ImportFromFile(profile, cmdline);
     86   }
     87   if (cmdline.HasSwitch(switches::kImport)) {
     88 #if defined(OS_WIN)
     89     return_code = ImportFromBrowser(profile, cmdline);
     90 #else
     91     NOTIMPLEMENTED();
     92 #endif
     93   }
     94   return return_code;
     95 }
     96 
     97 // static
     98 bool FirstRun::ProcessMasterPreferences(const FilePath& user_data_dir,
     99                                         MasterPrefs* out_prefs) {
    100   DCHECK(!user_data_dir.empty());
    101 
    102   // The standard location of the master prefs is next to the chrome binary.
    103   FilePath master_prefs;
    104   if (!PathService::Get(base::DIR_EXE, &master_prefs))
    105     return true;
    106   master_prefs = master_prefs.AppendASCII(installer::kDefaultMasterPrefs);
    107 
    108   installer::MasterPreferences prefs(master_prefs);
    109   if (!prefs.read_from_file())
    110     return true;
    111 
    112   out_prefs->new_tabs = prefs.GetFirstRunTabs();
    113 
    114   bool value = false;
    115 
    116 #if defined(OS_WIN)
    117   // RLZ is currently a Windows-only phenomenon.  When it comes to the Mac/
    118   // Linux, enable it here.
    119   if (!prefs.GetInt(installer::master_preferences::kDistroPingDelay,
    120                     &out_prefs->ping_delay)) {
    121     // 90 seconds is the default that we want to use in case master
    122     // preferences is missing, corrupt or ping_delay is missing.
    123     out_prefs->ping_delay = 90;
    124   }
    125 
    126   if (prefs.GetBool(installer::master_preferences::kRequireEula, &value) &&
    127       value) {
    128     // Show the post-installation EULA. This is done by setup.exe and the
    129     // result determines if we continue or not. We wait here until the user
    130     // dismisses the dialog.
    131 
    132     // The actual eula text is in a resource in chrome. We extract it to
    133     // a text file so setup.exe can use it as an inner frame.
    134     FilePath inner_html;
    135     if (WriteEULAtoTempFile(&inner_html)) {
    136       int retcode = 0;
    137       if (!LaunchSetupWithParam(installer::switches::kShowEula,
    138                                 inner_html.value(), &retcode) ||
    139           (retcode == installer::EULA_REJECTED)) {
    140         LOG(WARNING) << "EULA rejected. Fast exit.";
    141         ::ExitProcess(1);
    142       }
    143       if (retcode == installer::EULA_ACCEPTED) {
    144         VLOG(1) << "EULA : no collection";
    145         GoogleUpdateSettings::SetCollectStatsConsent(false);
    146       } else if (retcode == installer::EULA_ACCEPTED_OPT_IN) {
    147         VLOG(1) << "EULA : collection consent";
    148         GoogleUpdateSettings::SetCollectStatsConsent(true);
    149       }
    150     }
    151   }
    152 #endif
    153 
    154   if (prefs.GetBool(installer::master_preferences::kAltFirstRunBubble,
    155                     &value) && value) {
    156     FirstRun::SetOEMFirstRunBubblePref();
    157   }
    158 
    159   FilePath user_prefs = GetDefaultPrefFilePath(true, user_data_dir);
    160   if (user_prefs.empty())
    161     return true;
    162 
    163   // The master prefs are regular prefs so we can just copy the file
    164   // to the default place and they just work.
    165   if (!file_util::CopyFile(master_prefs, user_prefs))
    166     return true;
    167 
    168 #if defined(OS_WIN)
    169   DictionaryValue* extensions = 0;
    170   if (prefs.GetExtensionsBlock(&extensions)) {
    171     VLOG(1) << "Extensions block found in master preferences";
    172     DoDelayedInstallExtensions();
    173   }
    174 #endif
    175 
    176   if (prefs.GetBool(installer::master_preferences::kDistroImportSearchPref,
    177                     &value)) {
    178     if (value) {
    179       out_prefs->do_import_items |= importer::SEARCH_ENGINES;
    180     } else {
    181       out_prefs->dont_import_items |= importer::SEARCH_ENGINES;
    182     }
    183   }
    184 
    185   // Check to see if search engine logos should be randomized.
    186   if (prefs.GetBool(
    187           installer::master_preferences::
    188               kSearchEngineExperimentRandomizePref,
    189           &value) && value) {
    190     out_prefs->randomize_search_engine_experiment = true;
    191   }
    192 
    193   // If we're suppressing the first-run bubble, set that preference now.
    194   // Otherwise, wait until the user has completed first run to set it, so the
    195   // user is guaranteed to see the bubble iff he or she has completed the first
    196   // run process.
    197   if (prefs.GetBool(
    198           installer::master_preferences::kDistroSuppressFirstRunBubble,
    199           &value) && value)
    200     FirstRun::SetShowFirstRunBubblePref(false);
    201 
    202   if (prefs.GetBool(
    203           installer::master_preferences::kDistroImportHistoryPref,
    204           &value)) {
    205     if (value) {
    206       out_prefs->do_import_items |= importer::HISTORY;
    207     } else {
    208       out_prefs->dont_import_items |= importer::HISTORY;
    209     }
    210   }
    211 
    212   std::string not_used;
    213   out_prefs->homepage_defined = prefs.GetString(prefs::kHomePage, &not_used);
    214 
    215   if (prefs.GetBool(
    216           installer::master_preferences::kDistroImportHomePagePref,
    217           &value)) {
    218     if (value) {
    219       out_prefs->do_import_items |= importer::HOME_PAGE;
    220     } else {
    221       out_prefs->dont_import_items |= importer::HOME_PAGE;
    222     }
    223   }
    224 
    225   // Bookmarks are never imported unless specifically turned on.
    226   if (prefs.GetBool(
    227           installer::master_preferences::kDistroImportBookmarksPref,
    228           &value) && value) {
    229     out_prefs->do_import_items |= importer::FAVORITES;
    230   }
    231 
    232   if (prefs.GetBool(
    233           installer::master_preferences::kMakeChromeDefaultForUser,
    234           &value) && value) {
    235     out_prefs->make_chrome_default = true;
    236   }
    237 
    238   // TODO(mirandac): Refactor skip-first-run-ui process into regular first run
    239   // import process.  http://crbug.com/49647
    240   // Note we are skipping all other master preferences if skip-first-run-ui
    241   // is *not* specified. (That is, we continue only if skipping first run ui.)
    242   if (!prefs.GetBool(
    243           installer::master_preferences::kDistroSkipFirstRunPref,
    244           &value) || !value) {
    245     return true;
    246   }
    247 
    248 #if !defined(OS_WIN)
    249   // From here on we won't show first run so we need to do the work to show the
    250   // bubble anyway, unless it's already been explicitly suppressed.
    251   FirstRun::SetShowFirstRunBubblePref(true);
    252 #endif
    253 
    254   // We need to be able to create the first run sentinel or else we cannot
    255   // proceed because ImportSettings will launch the importer process which
    256   // would end up here if the sentinel is not present.
    257   if (!FirstRun::CreateSentinel())
    258     return false;
    259 
    260   if (prefs.GetBool(installer::master_preferences::kDistroShowWelcomePage,
    261                     &value) && value) {
    262     FirstRun::SetShowWelcomePagePref();
    263   }
    264 
    265   std::string import_bookmarks_path;
    266   prefs.GetString(
    267       installer::master_preferences::kDistroImportBookmarksFromFilePref,
    268       &import_bookmarks_path);
    269 
    270 #if defined(OS_WIN)
    271   if (!IsOrganicFirstRun()) {
    272     // If search engines aren't explicitly imported, don't import.
    273     if (!(out_prefs->do_import_items & importer::SEARCH_ENGINES)) {
    274       out_prefs->dont_import_items |= importer::SEARCH_ENGINES;
    275     }
    276     // If home page isn't explicitly imported, don't import.
    277     if (!(out_prefs->do_import_items & importer::HOME_PAGE)) {
    278       out_prefs->dont_import_items |= importer::HOME_PAGE;
    279     }
    280     // If history isn't explicitly forbidden, do import.
    281     if (!(out_prefs->dont_import_items & importer::HISTORY)) {
    282       out_prefs->do_import_items |= importer::HISTORY;
    283     }
    284   }
    285 
    286   if (out_prefs->do_import_items || !import_bookmarks_path.empty()) {
    287     // There is something to import from the default browser. This launches
    288     // the importer process and blocks until done or until it fails.
    289     scoped_refptr<ImporterList> importer_list(new ImporterList);
    290     importer_list->DetectSourceProfilesHack();
    291     if (!FirstRun::ImportSettings(NULL,
    292           importer_list->GetSourceProfileAt(0).importer_type,
    293           out_prefs->do_import_items,
    294           FilePath::FromWStringHack(UTF8ToWide(import_bookmarks_path)),
    295           true, NULL)) {
    296       LOG(WARNING) << "silent import failed";
    297     }
    298   }
    299 #else
    300   if (!import_bookmarks_path.empty()) {
    301     // There are bookmarks to import from a file.
    302     FilePath path = FilePath::FromWStringHack(UTF8ToWide(
    303         import_bookmarks_path));
    304     if (!FirstRun::ImportBookmarks(path)) {
    305       LOG(WARNING) << "silent bookmark import failed";
    306     }
    307   }
    308 #endif
    309 
    310   // Even on the first run we only allow for the user choice to take effect if
    311   // no policy has been set by the admin.
    312   if (!g_browser_process->local_state()->IsManagedPreference(
    313           prefs::kDefaultBrowserSettingEnabled)) {
    314     if (prefs.GetBool(
    315             installer::master_preferences::kMakeChromeDefaultForUser,
    316             &value) && value) {
    317       ShellIntegration::SetAsDefaultBrowser();
    318     }
    319   } else {
    320     if (g_browser_process->local_state()->GetBoolean(
    321             prefs::kDefaultBrowserSettingEnabled)) {
    322       ShellIntegration::SetAsDefaultBrowser();
    323     }
    324   }
    325 
    326   return false;
    327 }
    328 
    329 // static
    330 bool FirstRun::IsChromeFirstRun() {
    331   if (first_run_ != FIRST_RUN_UNKNOWN)
    332     return first_run_ == FIRST_RUN_TRUE;
    333 
    334   FilePath first_run_sentinel;
    335   if (!GetFirstRunSentinelFilePath(&first_run_sentinel) ||
    336       file_util::PathExists(first_run_sentinel)) {
    337     first_run_ = FIRST_RUN_FALSE;
    338     return false;
    339   }
    340   first_run_ = FIRST_RUN_TRUE;
    341   return true;
    342 }
    343 
    344 // static
    345 bool FirstRun::RemoveSentinel() {
    346   FilePath first_run_sentinel;
    347   if (!GetFirstRunSentinelFilePath(&first_run_sentinel))
    348     return false;
    349   return file_util::Delete(first_run_sentinel, false);
    350 }
    351 
    352 // static
    353 bool FirstRun::CreateSentinel() {
    354   FilePath first_run_sentinel;
    355   if (!GetFirstRunSentinelFilePath(&first_run_sentinel))
    356     return false;
    357   return file_util::WriteFile(first_run_sentinel, "", 0) != -1;
    358 }
    359 
    360 // static
    361 bool FirstRun::SetShowFirstRunBubblePref(bool show_bubble) {
    362   PrefService* local_state = g_browser_process->local_state();
    363   if (!local_state)
    364     return false;
    365   if (!local_state->FindPreference(prefs::kShouldShowFirstRunBubble)) {
    366     local_state->RegisterBooleanPref(prefs::kShouldShowFirstRunBubble, false);
    367     local_state->SetBoolean(prefs::kShouldShowFirstRunBubble, show_bubble);
    368   }
    369   return true;
    370 }
    371 
    372 // static
    373 bool FirstRun::SetShowWelcomePagePref() {
    374   PrefService* local_state = g_browser_process->local_state();
    375   if (!local_state)
    376     return false;
    377   if (!local_state->FindPreference(prefs::kShouldShowWelcomePage)) {
    378     local_state->RegisterBooleanPref(prefs::kShouldShowWelcomePage, false);
    379     local_state->SetBoolean(prefs::kShouldShowWelcomePage, true);
    380   }
    381   return true;
    382 }
    383 
    384 // static
    385 bool FirstRun::SetPersonalDataManagerFirstRunPref() {
    386   PrefService* local_state = g_browser_process->local_state();
    387   if (!local_state)
    388     return false;
    389   if (!local_state->FindPreference(
    390           prefs::kAutofillPersonalDataManagerFirstRun)) {
    391     local_state->RegisterBooleanPref(
    392         prefs::kAutofillPersonalDataManagerFirstRun, false);
    393     local_state->SetBoolean(prefs::kAutofillPersonalDataManagerFirstRun, true);
    394   }
    395   return true;
    396 }
    397 
    398 // static
    399 bool FirstRun::SearchEngineSelectorDisallowed() {
    400   // For now, the only case in which the search engine dialog should never be
    401   // shown is if the locale is Russia.
    402   std::string locale = g_browser_process->GetApplicationLocale();
    403   return (locale == "ru");
    404 }
    405 
    406 // static
    407 bool FirstRun::SetOEMFirstRunBubblePref() {
    408   PrefService* local_state = g_browser_process->local_state();
    409   if (!local_state)
    410     return false;
    411   if (!local_state->FindPreference(prefs::kShouldUseOEMFirstRunBubble)) {
    412     local_state->RegisterBooleanPref(prefs::kShouldUseOEMFirstRunBubble,
    413                                      false);
    414     local_state->SetBoolean(prefs::kShouldUseOEMFirstRunBubble, true);
    415   }
    416   return true;
    417 }
    418 
    419 // static
    420 bool FirstRun::SetMinimalFirstRunBubblePref() {
    421   PrefService* local_state = g_browser_process->local_state();
    422   if (!local_state)
    423     return false;
    424   if (!local_state->FindPreference(prefs::kShouldUseMinimalFirstRunBubble)) {
    425     local_state->RegisterBooleanPref(prefs::kShouldUseMinimalFirstRunBubble,
    426                                      false);
    427     local_state->SetBoolean(prefs::kShouldUseMinimalFirstRunBubble, true);
    428   }
    429   return true;
    430 }
    431 
    432 // static
    433 int FirstRun::ImportFromFile(Profile* profile, const CommandLine& cmdline) {
    434   FilePath file_path = cmdline.GetSwitchValuePath(switches::kImportFromFile);
    435   if (file_path.empty()) {
    436     NOTREACHED();
    437     return false;
    438   }
    439   scoped_refptr<ImporterHost> importer_host(new ImporterHost);
    440   importer_host->set_headless();
    441 
    442   importer::SourceProfile source_profile;
    443   source_profile.importer_type = importer::BOOKMARKS_HTML;
    444   source_profile.source_path = file_path;
    445 
    446   FirstRunImportObserver importer_observer;
    447   importer::ShowImportProgressDialog(NULL,
    448                                      importer::FAVORITES,
    449                                      importer_host,
    450                                      &importer_observer,
    451                                      source_profile,
    452                                      profile,
    453                                      true);
    454 
    455   importer_observer.RunLoop();
    456   return importer_observer.import_result();
    457 }
    458 
    459 // static
    460 bool FirstRun::GetFirstRunSentinelFilePath(FilePath* path) {
    461   FilePath first_run_sentinel;
    462 
    463 #if defined(OS_WIN)
    464   FilePath exe_path;
    465   if (!PathService::Get(base::DIR_EXE, &exe_path))
    466     return false;
    467   if (InstallUtil::IsPerUserInstall(exe_path.value().c_str())) {
    468     first_run_sentinel = exe_path;
    469   } else {
    470     if (!PathService::Get(chrome::DIR_USER_DATA, &first_run_sentinel))
    471       return false;
    472   }
    473 #else
    474   if (!PathService::Get(chrome::DIR_USER_DATA, &first_run_sentinel))
    475     return false;
    476 #endif
    477 
    478   *path = first_run_sentinel.AppendASCII(kSentinelFile);
    479   return true;
    480 }
    481 
    482 // static
    483 void FirstRun::AutoImport(
    484     Profile* profile,
    485     bool homepage_defined,
    486     int import_items,
    487     int dont_import_items,
    488     bool search_engine_experiment,
    489     bool randomize_search_engine_experiment,
    490     bool make_chrome_default,
    491     ProcessSingleton* process_singleton) {
    492   // We need to avoid dispatching new tabs when we are importing because
    493   // that will lead to data corruption or a crash. Because there is no UI for
    494   // the import process, we pass NULL as the window to bring to the foreground
    495   // when a CopyData message comes in; this causes the message to be silently
    496   // discarded, which is the correct behavior during the import process.
    497   process_singleton->Lock(NULL);
    498 
    499   PlatformSetup();
    500 
    501   FilePath local_state_path;
    502   PathService::Get(chrome::FILE_LOCAL_STATE, &local_state_path);
    503   bool local_state_file_exists = file_util::PathExists(local_state_path);
    504 
    505   scoped_refptr<ImporterHost> importer_host;
    506   // TODO(csilv,mirandac): Out-of-process import has only been qualified on
    507   // MacOS X, so we will only use it on that platform since it is required.
    508   // Remove this conditional logic once oop import is qualified for
    509   // Linux/Windows. http://crbug.com/22142
    510 #if defined(OS_MACOSX)
    511   importer_host = new ExternalProcessImporterHost;
    512 #else
    513   importer_host = new ImporterHost;
    514 #endif
    515 
    516   scoped_refptr<ImporterList> importer_list(new ImporterList);
    517   importer_list->DetectSourceProfilesHack();
    518 
    519   // Do import if there is an available profile for us to import.
    520   if (importer_list->count() > 0) {
    521     // Don't show the warning dialog if import fails.
    522     importer_host->set_headless();
    523     int items = 0;
    524 
    525     // History is always imported unless turned off in master_preferences.
    526     if (!(dont_import_items & importer::HISTORY))
    527       items = items | importer::HISTORY;
    528     // Home page is imported in organic builds only unless turned off or
    529     // defined in master_preferences.
    530     if (IsOrganicFirstRun()) {
    531       if (!(dont_import_items & importer::HOME_PAGE) && !homepage_defined)
    532         items = items | importer::HOME_PAGE;
    533     } else {
    534       if (import_items & importer::HOME_PAGE)
    535         items = items | importer::HOME_PAGE;
    536     }
    537     // Search engines are only imported in certain builds unless overridden
    538     // in master_preferences. Search engines are not imported automatically
    539     // if the user already has a user preferences directory.
    540     if (IsOrganicFirstRun()) {
    541       if (!(dont_import_items & importer::SEARCH_ENGINES) &&
    542           !local_state_file_exists) {
    543         items = items | importer::SEARCH_ENGINES;
    544       }
    545     } else if (import_items & importer::SEARCH_ENGINES) {
    546         items = items | importer::SEARCH_ENGINES;
    547     }
    548 
    549     // Bookmarks are never imported, unless turned on in master_preferences.
    550     if (import_items & importer::FAVORITES)
    551       items = items | importer::FAVORITES;
    552 
    553     ImportSettings(profile, importer_host, importer_list, items);
    554   }
    555 
    556   UserMetrics::RecordAction(UserMetricsAction("FirstRunDef_Accept"));
    557 
    558   // Launch the search engine dialog only for certain builds, and only if the
    559   // user has not already set preferences.
    560   if (IsOrganicFirstRun() && !local_state_file_exists) {
    561     // The home page string may be set in the preferences, but the user should
    562     // initially use Chrome with the NTP as home page in organic builds.
    563     profile->GetPrefs()->SetBoolean(prefs::kHomePageIsNewTabPage, true);
    564     first_run::ShowFirstRunDialog(profile, randomize_search_engine_experiment);
    565   }
    566 
    567   if (make_chrome_default)
    568     ShellIntegration::SetAsDefaultBrowser();
    569 
    570   // Don't display the minimal bubble if there is no default search provider.
    571   TemplateURLModel* search_engines_model = profile->GetTemplateURLModel();
    572   if (search_engines_model &&
    573       search_engines_model->GetDefaultSearchProvider()) {
    574     FirstRun::SetShowFirstRunBubblePref(true);
    575     // Set the first run bubble to minimal.
    576     FirstRun::SetMinimalFirstRunBubblePref();
    577   }
    578   FirstRun::SetShowWelcomePagePref();
    579   FirstRun::SetPersonalDataManagerFirstRunPref();
    580 
    581   process_singleton->Unlock();
    582   FirstRun::CreateSentinel();
    583 }
    584 
    585 #if defined(OS_POSIX)
    586 namespace {
    587 
    588 // This class acts as an observer for the ImporterProgressObserver::ImportEnded
    589 // callback. When the import process is started, certain errors may cause
    590 // ImportEnded() to be called synchronously, but the typical case is that
    591 // ImportEnded() is called asynchronously. Thus we have to handle both cases.
    592 class ImportEndedObserver : public importer::ImporterProgressObserver {
    593  public:
    594   ImportEndedObserver() : ended_(false),
    595                           should_quit_message_loop_(false) {}
    596   virtual ~ImportEndedObserver() {}
    597 
    598   // importer::ImporterProgressObserver:
    599   virtual void ImportStarted() OVERRIDE {}
    600   virtual void ImportItemStarted(importer::ImportItem item) OVERRIDE {}
    601   virtual void ImportItemEnded(importer::ImportItem item) OVERRIDE {}
    602   virtual void ImportEnded() OVERRIDE {
    603     ended_ = true;
    604     if (should_quit_message_loop_)
    605       MessageLoop::current()->Quit();
    606   }
    607 
    608   void set_should_quit_message_loop() {
    609     should_quit_message_loop_ = true;
    610   }
    611 
    612   bool ended() {
    613     return ended_;
    614   }
    615 
    616  private:
    617   // Set if the import has ended.
    618   bool ended_;
    619 
    620   // Set by the client (via set_should_quit_message_loop) if, when the import
    621   // ends, this class should quit the message loop.
    622   bool should_quit_message_loop_;
    623 };
    624 
    625 }  // namespace
    626 
    627 // static
    628 bool FirstRun::ImportSettings(Profile* profile,
    629                               scoped_refptr<ImporterHost> importer_host,
    630                               scoped_refptr<ImporterList> importer_list,
    631                               int items_to_import) {
    632   const importer::SourceProfile& source_profile =
    633       importer_list->GetSourceProfileAt(0);
    634 
    635   // Ensure that importers aren't requested to import items that they do not
    636   // support.
    637   items_to_import &= source_profile.services_supported;
    638 
    639   scoped_ptr<ImportEndedObserver> observer(new ImportEndedObserver);
    640   importer_host->SetObserver(observer.get());
    641   importer_host->StartImportSettings(source_profile,
    642                                      profile,
    643                                      items_to_import,
    644                                      new ProfileWriter(profile),
    645                                      true);
    646   // If the import process has not errored out, block on it.
    647   if (!observer->ended()) {
    648     observer->set_should_quit_message_loop();
    649     MessageLoop::current()->Run();
    650   }
    651 
    652   // Unfortunately there's no success/fail signal in ImporterHost.
    653   return true;
    654 }
    655 
    656 #endif  // OS_POSIX
    657