blob: 39937969924e169c15c6e46389fd88e9ec0b19e5 [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/webdata_services/web_data_service_factory.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/metrics/histogram_functions.h"
#include "base/no_destructor.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/incognito_helpers.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/sql_init_error_message_ids.h"
#include "chrome/browser/ui/profiles/profile_error_dialog.h"
#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
#include "components/plus_addresses/webdata/plus_address_webdata_service.h"
#include "components/prefs/pref_service.h"
#include "components/search_engines/keyword_web_data_service.h"
#include "components/signin/public/base/signin_pref_names.h"
#include "components/signin/public/webdata/token_web_data.h"
#include "components/webdata_services/web_data_service_wrapper.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
namespace {
// Converts a WebDataServiceWrapper::ErrorType to ProfileErrorType.
ProfileErrorType ProfileErrorFromWebDataServiceWrapperError(
WebDataServiceWrapper::ErrorType error_type) {
switch (error_type) {
case WebDataServiceWrapper::ERROR_LOADING_AUTOFILL:
return ProfileErrorType::DB_AUTOFILL_WEB_DATA;
case WebDataServiceWrapper::ERROR_LOADING_ACCOUNT_AUTOFILL:
return ProfileErrorType::DB_ACCOUNT_AUTOFILL_WEB_DATA;
case WebDataServiceWrapper::ERROR_LOADING_KEYWORD:
return ProfileErrorType::DB_KEYWORD_WEB_DATA;
case WebDataServiceWrapper::ERROR_LOADING_TOKEN:
return ProfileErrorType::DB_TOKEN_WEB_DATA;
case WebDataServiceWrapper::ERROR_LOADING_PASSWORD:
return ProfileErrorType::DB_WEB_DATA;
case WebDataServiceWrapper::ERROR_LOADING_PAYMENT_MANIFEST:
return ProfileErrorType::DB_PAYMENT_MANIFEST_WEB_DATA;
case WebDataServiceWrapper::ERROR_LOADING_PLUS_ADDRESS:
return ProfileErrorType::DB_WEB_DATA;
default:
NOTREACHED() << "Unknown WebDataServiceWrapper::ErrorType: "
<< error_type;
}
}
// Callback to show error dialog on profile load error.
void ProfileErrorCallback(WebDataServiceWrapper::ErrorType error_type,
sql::InitStatus status,
const std::string& diagnostics) {
ShowProfileErrorDialog(ProfileErrorFromWebDataServiceWrapperError(error_type),
SqlInitStatusToMessageId(status), diagnostics);
}
// The logic and metrics below are used on desktop only, as on mobile autofill
// account data is always persisted on disk.
#if !BUILDFLAG(IS_ANDROID)
// List of possible outcomes for the logic that determines whether the autofill
// account web database should be persisted on disk or in-memory only.
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
// LINT.IfChange(WebDatabaseAutofillAccountStorageResult)
enum class AutofillAccountStorageResult {
kInMemory_FlagDisabled = 0,
kInMemory_SignedInImplicitly = 1,
kOnDisk_SignedOut = 2,
kOnDisk_SignedInExplicitly = 3,
kOnDisk_SyncFeatureEnabled = 4,
kMaxValue = kOnDisk_SyncFeatureEnabled
};
// LINT.ThenChange(/tools/metrics/histograms/metadata/autofill/enums.xml:WebDatabaseAutofillAccountStorageResult)
// See `ShouldUseInMemoryAutofillAccountDatabase()` for details about how this
// function is useful. Instead of returning a boolean, this function returns an
// enum that is useful for logging metrics.
AutofillAccountStorageResult DetermineAutofillAccountStorage(
PrefService* pref_service) {
CHECK(pref_service);
// The interpretation of the pref mimics what PrimaryAccountManager's
// constructor does.
const bool is_signed_in =
!pref_service->GetString(::prefs::kGoogleServicesAccountId).empty();
// If the user is signed out during profile startup, as per switch above
// being enabled, any new sign-ins will involve an explicit sign-in (i.e.
// interaction with native UI). In this case, on-disk storage is appropriate.
if (!is_signed_in) {
return AutofillAccountStorageResult::kOnDisk_SignedOut;
}
// It is possible that the user already is in an explicit sign-in state. In
// this case, on-disk storage is appropriate, as any additional future
// sign-ins (if the user first signs out) are guaranteed to be explicit
// sign-ins too.
if (pref_service->GetBoolean(::prefs::kExplicitBrowserSignin)) {
return AutofillAccountStorageResult::kOnDisk_SignedInExplicitly;
}
// The interpretation of the pref mimics what PrimaryAccountManager's
// constructor does.
const bool is_consented_to_sync =
pref_service->GetBoolean(::prefs::kGoogleServicesConsentedToSync);
// If Sync (the feature) is on, the account storage isn't currently used. This
// is because the only way to activate the account storate requires signing
// out first, which means the predicate can return false as per earlier
// rationale. With one exception: managed profiles may turn sync off without
// signing out. Either way, having turned sync on implies the user interacted
// explicitly with a sync UI, so in this particular context it is no different
// from explicit sign-in, and on-disk storage is appropriate.
if (is_consented_to_sync) {
return AutofillAccountStorageResult::kOnDisk_SyncFeatureEnabled;
}
// The remaining case implies a legacy signed-in-non-syncing state with
// implicit sign-in, which means the user signed in before the latest feature
// flags rolled out. This is the only case where in-memory storage should be
// used.
//
// Note that, during the lifetime of the browser/profile, it is still possible
// that the users signs out and signs back in, where the latter is guaranteed
// to be an explicit sign-in. In this case, it would be theoretically better
// to immediately switch to on-disk storage, but this isn't possible once a
// profile is initialized (as this predicate only gets evaluated once).
// Conveniently, it is also harmless to use the in-memory storage until the
// next browser restart, given that this is a one-off transition (upon restart
// the code would run into one of the cases listed earlier that return false).
return AutofillAccountStorageResult::kInMemory_SignedInImplicitly;
}
#endif // !BUILDFLAG(IS_ANDROID)
// Predicate that determines whether autofill should use an in-memory database
// for account data (i.e. data corresponding to signed-in-non-syncing users) as
// opposed to on-disk storage.
//
// The desired product behavior in the strictest form would lead to returning
// different values during the lifetime of a profile, if the user's sign-in
// state changes. However, it is possible to make a good decision during profile
// startup, based on IdentityManager's state machine and its representation in
// prefs.
bool ShouldUseInMemoryAutofillAccountDatabase(PrefService* pref_service) {
#if BUILDFLAG(IS_ANDROID)
// On Android (and iOS), the account storage is persisted on disk.
return false;
#else // BUILDFLAG(IS_ANDROID)
const AutofillAccountStorageResult result =
DetermineAutofillAccountStorage(pref_service);
base::UmaHistogramEnumeration("WebDatabase.AutofillAccountStorage", result);
switch (result) {
case AutofillAccountStorageResult::kInMemory_FlagDisabled:
case AutofillAccountStorageResult::kInMemory_SignedInImplicitly:
return true;
case AutofillAccountStorageResult::kOnDisk_SignedOut:
case AutofillAccountStorageResult::kOnDisk_SignedInExplicitly:
case AutofillAccountStorageResult::kOnDisk_SyncFeatureEnabled:
return false;
}
NOTREACHED();
#endif // BUILDFLAG(IS_ANDROID)
}
std::unique_ptr<KeyedService> BuildWebDataService(
content::BrowserContext* context) {
const base::FilePath& profile_path = context->GetPath();
Profile* profile = Profile::FromBrowserContext(context);
return std::make_unique<WebDataServiceWrapper>(
profile_path, g_browser_process->GetApplicationLocale(),
content::GetUIThreadTaskRunner({}),
base::BindRepeating(&ProfileErrorCallback),
g_browser_process->os_crypt_async(),
ShouldUseInMemoryAutofillAccountDatabase(profile->GetPrefs()));
}
} // namespace
WebDataServiceFactory::WebDataServiceFactory() = default;
WebDataServiceFactory::~WebDataServiceFactory() = default;
// static
WebDataServiceWrapper* WebDataServiceFactory::GetForProfile(
Profile* profile,
ServiceAccessType access_type) {
return GetForBrowserContext(profile, access_type);
}
// static
WebDataServiceWrapper* WebDataServiceFactory::GetForProfileIfExists(
Profile* profile,
ServiceAccessType access_type) {
return GetForBrowserContextIfExists(profile, access_type);
}
// static
scoped_refptr<autofill::AutofillWebDataService>
WebDataServiceFactory::GetAutofillWebDataForProfile(
Profile* profile,
ServiceAccessType access_type) {
WebDataServiceWrapper* wrapper =
WebDataServiceFactory::GetForProfile(profile, access_type);
// |wrapper| can be null in tests.
return wrapper ? wrapper->GetProfileAutofillWebData()
: scoped_refptr<autofill::AutofillWebDataService>(nullptr);
}
// static
scoped_refptr<autofill::AutofillWebDataService>
WebDataServiceFactory::GetAutofillWebDataForAccount(
Profile* profile,
ServiceAccessType access_type) {
WebDataServiceWrapper* wrapper =
WebDataServiceFactory::GetForProfile(profile, access_type);
// |wrapper| can be null in tests.
return wrapper ? wrapper->GetAccountAutofillWebData()
: scoped_refptr<autofill::AutofillWebDataService>(nullptr);
}
// static
scoped_refptr<KeywordWebDataService>
WebDataServiceFactory::GetKeywordWebDataForProfile(
Profile* profile,
ServiceAccessType access_type) {
WebDataServiceWrapper* wrapper =
WebDataServiceFactory::GetForProfile(profile, access_type);
// |wrapper| can be null in tests.
return wrapper ? wrapper->GetKeywordWebData()
: scoped_refptr<KeywordWebDataService>(nullptr);
}
// static
scoped_refptr<plus_addresses::PlusAddressWebDataService>
WebDataServiceFactory::GetPlusAddressWebDataForProfile(
Profile* profile,
ServiceAccessType access_type) {
WebDataServiceWrapper* wrapper =
WebDataServiceFactory::GetForProfile(profile, access_type);
// |wrapper| can be null in tests.
return wrapper ? wrapper->GetPlusAddressWebData()
: scoped_refptr<plus_addresses::PlusAddressWebDataService>(
nullptr);
}
// static
scoped_refptr<TokenWebData> WebDataServiceFactory::GetTokenWebDataForProfile(
Profile* profile,
ServiceAccessType access_type) {
WebDataServiceWrapper* wrapper =
WebDataServiceFactory::GetForProfile(profile, access_type);
// |wrapper| can be null in tests.
return wrapper ? wrapper->GetTokenWebData()
: scoped_refptr<TokenWebData>(nullptr);
}
// static
WebDataServiceFactory* WebDataServiceFactory::GetInstance() {
static base::NoDestructor<WebDataServiceFactory> instance;
return instance.get();
}
// static
BrowserContextKeyedServiceFactory::TestingFactory
WebDataServiceFactory::GetDefaultFactory() {
return base::BindRepeating(&BuildWebDataService);
}
content::BrowserContext* WebDataServiceFactory::GetBrowserContextToUse(
content::BrowserContext* context) const {
return GetBrowserContextRedirectedInIncognito(context);
}
std::unique_ptr<KeyedService>
WebDataServiceFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const {
return BuildWebDataService(context);
}
bool WebDataServiceFactory::ServiceIsNULLWhileTesting() const {
return true;
}