#include "chrome/browser/first_run/first_run.h"
#include <algorithm>
#include <memory>
#include <string>
#include <tuple>
#include <utility>
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics.h"
#include "base/no_destructor.h"
#include "base/one_shot_event.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/extensions/updater/extension_updater.h"
#include "chrome/browser/first_run/first_run_features.h"
#include "chrome/browser/first_run/first_run_internal.h"
#include "chrome/browser/google/google_brand.h"
#include "chrome/browser/headless/headless_mode_util.h"
#include "chrome/browser/importer/external_process_importer_host.h"
#include "chrome/browser/importer/importer_list.h"
#include "chrome/browser/importer/importer_progress_observer.h"
#include "chrome/browser/importer/importer_uma.h"
#include "chrome/browser/importer/profile_writer.h"
#include "chrome/browser/prefs/chrome_pref_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profiles_state.h"
#include "chrome/browser/shell_integration.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/global_error/global_error_service.h"
#include "chrome/browser/ui/global_error/global_error_service_factory.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/installer/util/initial_preferences.h"
#include "chrome/installer/util/initial_preferences_constants.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
#include "components/startup_metric_utils/browser/startup_metric_utils.h"
#include "extensions/buildflags/buildflags.h"
#include "google_apis/gaia/gaia_auth_util.h"
#include "url/gurl.h"
#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "chrome/browser/browser_features.h"
#include "components/crx_file/id_util.h"
#include "extensions/browser/pref_names.h"
#endif
namespace content {
class BrowserContext;
}
using base::UserMetricsAction;
namespace {
uint16_t g_auto_import_state = first_run::AUTO_IMPORT_NONE;
first_run::internal::FirstRunState g_first_run =
first_run::internal::FIRST_RUN_UNKNOWN;
base::Time g_cached_sentinel_creation_time;
class ImportEndedObserver : public importer::ImporterProgressObserver {
public:
ImportEndedObserver() = default;
ImportEndedObserver(const ImportEndedObserver&) = delete;
ImportEndedObserver& operator=(const ImportEndedObserver&) = delete;
void ImportStarted() override {}
void ImportItemStarted(user_data_importer::ImportItem item) override {}
void ImportItemEnded(user_data_importer::ImportItem item) override {}
void ImportEnded() override {
ended_ = true;
if (callback_for_import_end_)
std::move(callback_for_import_end_).Run();
}
void set_callback_for_import_end(base::OnceClosure callback) {
callback_for_import_end_ = std::move(callback);
}
bool ended() const {
return ended_;
}
private:
bool ended_ = false;
base::OnceClosure callback_for_import_end_;
};
void ImportFromSourceProfile(
const user_data_importer::SourceProfile& source_profile,
Profile* target_profile,
uint16_t items_to_import) {
ExternalProcessImporterHost* importer_host =
new ExternalProcessImporterHost;
importer_host->set_headless();
ImportEndedObserver observer;
importer_host->set_observer(&observer);
importer_host->StartImportSettings(source_profile,
target_profile,
items_to_import,
new ProfileWriter(target_profile));
if (!observer.ended()) {
base::RunLoop loop;
observer.set_callback_for_import_end(loop.QuitClosure());
loop.Run();
}
}
void ImportFromFile(Profile* profile,
const std::string& import_bookmarks_path) {
user_data_importer::SourceProfile source_profile;
source_profile.importer_type = user_data_importer::TYPE_BOOKMARKS_FILE;
const base::FilePath::StringType& import_bookmarks_path_str =
#if BUILDFLAG(IS_WIN)
base::UTF8ToWide(import_bookmarks_path);
#else
import_bookmarks_path;
#endif
source_profile.source_path = base::FilePath(import_bookmarks_path_str);
ImportFromSourceProfile(source_profile, profile,
user_data_importer::FAVORITES);
g_auto_import_state |= first_run::AUTO_IMPORT_BOOKMARKS_FILE_IMPORTED;
}
void ImportSettings(Profile* profile,
std::unique_ptr<ImporterList> importer_list,
uint16_t items_to_import) {
DCHECK(items_to_import);
const user_data_importer::SourceProfile& source_profile =
importer_list->GetSourceProfileAt(0);
items_to_import &= source_profile.services_supported;
if (items_to_import)
ImportFromSourceProfile(source_profile, profile, items_to_import);
g_auto_import_state |= first_run::AUTO_IMPORT_PROFILE_IMPORTED;
}
GURL UrlFromString(const std::string& in) {
return GURL(in);
}
void ConvertStringVectorToGURLVector(
const std::vector<std::string>& src,
std::vector<GURL>* ret) {
ret->resize(src.size());
std::ranges::transform(src, ret->begin(), &UrlFromString);
}
base::FilePath& GetInitialPrefsPathForTesting() {
static base::NoDestructor<base::FilePath> s;
return *s;
}
void ProcessDefaultBrowserPolicy(bool make_chrome_default_for_user) {
if (shell_integration::GetDefaultBrowserSetPermission() ==
shell_integration::SET_DEFAULT_UNATTENDED) {
if (g_browser_process->local_state()->IsManagedPreference(
prefs::kDefaultBrowserSettingEnabled)) {
if (g_browser_process->local_state()->GetBoolean(
prefs::kDefaultBrowserSettingEnabled)) {
shell_integration::SetAsDefaultBrowser();
}
} else if (make_chrome_default_for_user) {
shell_integration::SetAsDefaultBrowser();
}
}
}
bool GetFirstRunSentinelFilePath(base::FilePath* path) {
base::FilePath user_data_dir;
if (!base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir))
return false;
*path = user_data_dir.Append(chrome::kFirstRunSentinel);
return true;
}
startup_metric_utils::FirstRunSentinelCreationResult CreateSentinel() {
base::FilePath first_run_sentinel;
if (!GetFirstRunSentinelFilePath(&first_run_sentinel)) {
return startup_metric_utils::FirstRunSentinelCreationResult::
kFailedToGetPath;
}
if (base::PathExists(first_run_sentinel)) {
return startup_metric_utils::FirstRunSentinelCreationResult::
kFilePathExists;
}
if (!base::WriteFile(first_run_sentinel, "")) {
return startup_metric_utils::FirstRunSentinelCreationResult::
kFileSystemError;
}
return startup_metric_utils::FirstRunSentinelCreationResult::kSuccess;
}
base::Time ReadFirstRunSentinelCreationTime() {
base::Time first_run_sentinel_creation_time = base::Time();
base::FilePath first_run_sentinel;
if (GetFirstRunSentinelFilePath(&first_run_sentinel)) {
base::File::Info info;
if (base::GetFileInfo(first_run_sentinel, &info))
first_run_sentinel_creation_time = info.creation_time;
}
return first_run_sentinel_creation_time;
}
bool IsFirstRunSentinelPresent() {
base::FilePath sentinel;
return !GetFirstRunSentinelFilePath(&sentinel) || base::PathExists(sentinel);
}
}
namespace first_run {
namespace internal {
void SetupInitialPrefsFromInstallPrefs(
const installer::InitialPreferences& install_prefs,
MasterPrefs* out_prefs) {
ConvertStringVectorToGURLVector(
install_prefs.GetFirstRunTabs(), &out_prefs->new_tabs);
bool value = false;
if (install_prefs.GetBool(
installer::initial_preferences::kMakeChromeDefaultForUser, &value) &&
value) {
out_prefs->make_chrome_default_for_user = true;
}
install_prefs.GetString(
installer::initial_preferences::kDistroImportBookmarksFromFilePref,
&out_prefs->import_bookmarks_path);
install_prefs.GetString(
installer::initial_preferences::kDistroSuppressDefaultBrowserPromptPref,
&out_prefs->suppress_default_browser_prompt_for_version);
if (base::FeatureList::IsEnabled(features::kBookmarksImportOnFirstRun)) {
const base::Value::Dict* bookmarks_dict = install_prefs.GetBookmarksBlock();
if (bookmarks_dict) {
out_prefs->import_bookmarks_dict = bookmarks_dict->Clone();
}
}
#if BUILDFLAG(ENABLE_EXTENSIONS)
if (base::FeatureList::IsEnabled(features::kInitialExternalExtensions)) {
out_prefs->initial_extensions_provider_name =
install_prefs.GetInitialExtensionsProviderName();
if (const base::Value::List* initial_extensions =
install_prefs.GetInitialExtensionsList()) {
out_prefs->initial_extensions = initial_extensions->Clone();
}
}
#endif
#if BUILDFLAG(IS_MAC)
if (install_prefs.GetBool(prefs::kConfirmToQuitEnabled, &value) && value)
out_prefs->confirm_to_quit = true;
#endif
}
#if !BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_BSD) && \
!BUILDFLAG(IS_OHOS)
bool IsOrganicFirstRun() {
std::string brand;
google_brand::GetBrand(&brand);
return google_brand::IsOrganicFirstRun(brand);
}
#endif
FirstRunState DetermineFirstRunState(bool has_sentinel,
bool force_first_run,
bool no_first_run) {
return (force_first_run || (!has_sentinel && !no_first_run))
? FIRST_RUN_TRUE
: FIRST_RUN_FALSE;
}
}
MasterPrefs::MasterPrefs() = default;
MasterPrefs::~MasterPrefs() = default;
void RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) {
registry->RegisterBooleanPref(prefs::kImportAutofillFormData, false);
registry->RegisterBooleanPref(prefs::kImportBookmarks, false);
registry->RegisterBooleanPref(prefs::kImportHistory, false);
registry->RegisterBooleanPref(prefs::kImportHomepage, false);
registry->RegisterBooleanPref(prefs::kImportSavedPasswords, false);
registry->RegisterBooleanPref(prefs::kImportSearchEngine, false);
}
bool IsChromeFirstRun() {
if (g_first_run == internal::FIRST_RUN_UNKNOWN) {
const base::CommandLine* command_line =
base::CommandLine::ForCurrentProcess();
g_first_run = internal::DetermineFirstRunState(
IsFirstRunSentinelPresent(),
command_line->HasSwitch(switches::kForceFirstRun),
command_line->HasSwitch(switches::kNoFirstRun));
}
return g_first_run == internal::FIRST_RUN_TRUE;
}
#if BUILDFLAG(IS_MAC)
bool IsFirstRunSuppressed(const base::CommandLine& command_line) {
return command_line.HasSwitch(switches::kNoFirstRun);
}
#endif
void CreateSentinelIfNeeded() {
if (IsChromeFirstRun()) {
auto sentinel_creation_result = CreateSentinel();
startup_metric_utils::GetBrowser().RecordFirstRunSentinelCreation(
sentinel_creation_result);
}
std::ignore = GetFirstRunSentinelCreationTime();
}
base::Time GetFirstRunSentinelCreationTime() {
if (g_cached_sentinel_creation_time.is_null())
g_cached_sentinel_creation_time = ReadFirstRunSentinelCreationTime();
return g_cached_sentinel_creation_time;
}
void ResetCachedSentinelDataForTesting() {
g_cached_sentinel_creation_time = base::Time();
g_first_run = first_run::internal::FIRST_RUN_UNKNOWN;
}
void SetInitialPrefsPathForTesting(const base::FilePath& initial_prefs) {
GetInitialPrefsPathForTesting() = initial_prefs;
}
std::unique_ptr<installer::InitialPreferences> LoadInitialPrefs() {
base::FilePath initial_prefs_path;
if (!GetInitialPrefsPathForTesting().empty()) {
initial_prefs_path = GetInitialPrefsPathForTesting();
} else {
initial_prefs_path =
base::FilePath(first_run::internal::InitialPrefsPath());
}
if (initial_prefs_path.empty())
return nullptr;
auto initial_prefs =
std::make_unique<installer::InitialPreferences>(initial_prefs_path);
if (!initial_prefs->read_from_file())
return nullptr;
return initial_prefs;
}
ProcessInitialPreferencesResult ProcessInitialPreferences(
const base::FilePath& user_data_dir,
std::unique_ptr<installer::InitialPreferences> initial_prefs,
MasterPrefs* out_prefs) {
DCHECK(!user_data_dir.empty());
if (initial_prefs.get()) {
if (!headless::IsHeadlessMode() &&
!internal::ShowPostInstallEULAIfNeeded(initial_prefs.get())) {
return EULA_EXIT_NOW;
}
base::Value::Dict initial_dictionary =
initial_prefs->initial_dictionary().Clone();
initial_dictionary.Remove(installer::initial_preferences::kDistroDict);
initial_dictionary.Remove(installer::initial_preferences::kBookmarksBlock);
#if BUILDFLAG(ENABLE_EXTENSIONS)
initial_dictionary.RemoveByDottedPath(
installer::initial_preferences::kExtensionsBlock);
#endif
if (!chrome_prefs::InitializePrefsFromMasterPrefs(
profiles::GetDefaultProfileDir(user_data_dir),
std::move(initial_dictionary),
g_browser_process->os_crypt_async())) {
DLOG(ERROR) << "Failed to initialize from initial preferences.";
}
internal::SetupInitialPrefsFromInstallPrefs(*initial_prefs, out_prefs);
}
return FIRST_RUN_PROCEED;
}
void AutoImport(
Profile* profile,
const std::string& import_bookmarks_path) {
g_auto_import_state |= AUTO_IMPORT_CALLED;
PrefService* prefs = profile->GetPrefs();
uint16_t items_to_import = 0;
static constexpr struct {
const char* pref_path;
user_data_importer::ImportItem bit;
} kImportItems[] = {
{prefs::kImportAutofillFormData, user_data_importer::AUTOFILL_FORM_DATA},
{prefs::kImportBookmarks, user_data_importer::FAVORITES},
{prefs::kImportHistory, user_data_importer::HISTORY},
{prefs::kImportHomepage, user_data_importer::HOME_PAGE},
{prefs::kImportSavedPasswords, user_data_importer::PASSWORDS},
{prefs::kImportSearchEngine, user_data_importer::SEARCH_ENGINES},
};
for (const auto& import_item : kImportItems) {
if (prefs->GetBoolean(import_item.pref_path))
items_to_import |= import_item.bit;
}
if (items_to_import) {
base::RunLoop run_loop;
auto importer_list = std::make_unique<ImporterList>();
importer_list->DetectSourceProfiles(
g_browser_process->GetApplicationLocale(),
false,
run_loop.QuitClosure());
run_loop.Run();
if (importer_list->count() > 0) {
importer::LogImporterUseToMetrics(
"AutoImport", importer_list->GetSourceProfileAt(0).importer_type);
ImportSettings(profile, std::move(importer_list), items_to_import);
}
}
if (!import_bookmarks_path.empty())
ImportFromFile(profile, import_bookmarks_path);
}
void DoPostImportTasks(bool make_chrome_default_for_user) {
ProcessDefaultBrowserPolicy(make_chrome_default_for_user);
internal::DoPostImportPlatformSpecificTasks();
}
uint16_t auto_import_state() {
return g_auto_import_state;
}
}