blob: 0e247e7d2e338cf57cdcec65b1d84fc7121e3b2b [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/autofill/core/browser/strike_database.h"
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/metrics/histogram_functions.h"
#include "base/task/post_task.h"
#include "base/time/time.h"
#include "components/autofill/core/browser/proto/strike_data.pb.h"
#include "components/autofill/core/common/autofill_clock.h"
#include "components/leveldb_proto/public/proto_database_provider.h"
namespace autofill {
namespace {
const char kDatabaseClientName[] = "StrikeService";
const int kMaxInitAttempts = 3;
} // namespace
StrikeDatabase::StrikeDatabase(const base::FilePath& database_dir)
: db_(leveldb_proto::ProtoDatabaseProvider::CreateUniqueDB<StrikeData>(
base::CreateSequencedTaskRunnerWithTraits(
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}))),
database_dir_(database_dir),
weak_ptr_factory_(this) {
db_->Init(kDatabaseClientName, database_dir,
leveldb_proto::CreateSimpleOptions(),
base::BindRepeating(&StrikeDatabase::OnDatabaseInit,
weak_ptr_factory_.GetWeakPtr()));
}
StrikeDatabase::~StrikeDatabase() {}
int StrikeDatabase::AddStrikes(int strikes_increase, const std::string key) {
DCHECK(strikes_increase > 0);
int num_strikes =
strike_map_cache_.count(key) // Cache has entry for |key|.
? strike_map_cache_[key].num_strikes() + strikes_increase
: strikes_increase;
SetStrikeData(key, num_strikes);
return num_strikes;
}
int StrikeDatabase::RemoveStrikes(int strikes_decrease, const std::string key) {
DCHECK(strikes_decrease > 0);
DCHECK(strike_map_cache_.count(key));
int num_strikes = strike_map_cache_[key].num_strikes() - strikes_decrease;
if (num_strikes < 1) {
ClearStrikes(key);
return 0;
}
SetStrikeData(key, num_strikes);
return num_strikes;
}
int StrikeDatabase::GetStrikes(const std::string key) {
return strike_map_cache_.count(key) // Cache contains entry for |key|.
? strike_map_cache_[key].num_strikes()
: 0;
}
void StrikeDatabase::ClearStrikes(const std::string key) {
strike_map_cache_.erase(key);
ClearAllProtoStrikesForKey(key, base::DoNothing());
}
StrikeDatabase::StrikeDatabase()
: db_(nullptr),
database_dir_(base::FilePath(nullptr)),
weak_ptr_factory_(this) {}
void StrikeDatabase::OnDatabaseInit(bool success) {
database_initialized_ = success;
if (!success) {
base::UmaHistogramCounts100(
"Autofill.StrikeDatabase.StrikeDatabaseInitFailed", num_init_attempts_);
if (num_init_attempts_ < kMaxInitAttempts) {
num_init_attempts_++;
db_->Init(kDatabaseClientName, database_dir_,
leveldb_proto::CreateSimpleOptions(),
base::BindRepeating(&StrikeDatabase::OnDatabaseInit,
weak_ptr_factory_.GetWeakPtr()));
}
return;
}
db_->LoadKeysAndEntries(
base::BindRepeating(&StrikeDatabase::OnDatabaseLoadKeysAndEntries,
weak_ptr_factory_.GetWeakPtr()));
}
void StrikeDatabase::OnDatabaseLoadKeysAndEntries(
bool success,
std::unique_ptr<std::map<std::string, StrikeData>> entries) {
if (!success) {
database_initialized_ = false;
return;
}
strike_map_cache_.insert(entries->begin(), entries->end());
}
void StrikeDatabase::SetStrikeData(const std::string key, int num_strikes) {
StrikeData data;
data.set_num_strikes(num_strikes);
data.set_last_update_timestamp(
AutofillClock::Now().ToDeltaSinceWindowsEpoch().InMicroseconds());
UpdateCache(key, data);
SetProtoStrikeData(key, data, base::DoNothing());
}
void StrikeDatabase::GetProtoStrikes(const std::string key,
const StrikesCallback& outer_callback) {
if (!database_initialized_) {
outer_callback.Run(false);
return;
}
GetProtoStrikeData(
key,
base::BindRepeating(&StrikeDatabase::OnGetProtoStrikes,
base::Unretained(this), std::move(outer_callback)));
}
void StrikeDatabase::ClearAllProtoStrikes(
const ClearStrikesCallback& outer_callback) {
if (!database_initialized_) {
outer_callback.Run(false);
return;
}
// For deleting all, filter method always returns true.
db_->UpdateEntriesWithRemoveFilter(
std::make_unique<StrikeDataProto::KeyEntryVector>(),
base::BindRepeating([](const std::string& key) { return true; }),
outer_callback);
}
void StrikeDatabase::ClearAllProtoStrikesForKey(
const std::string& key,
const ClearStrikesCallback& outer_callback) {
if (!database_initialized_) {
outer_callback.Run(false);
return;
}
std::unique_ptr<std::vector<std::string>> keys_to_remove(
new std::vector<std::string>());
keys_to_remove->push_back(key);
db_->UpdateEntries(
/*entries_to_save=*/std::make_unique<
leveldb_proto::ProtoDatabase<StrikeData>::KeyEntryVector>(),
/*keys_to_remove=*/std::move(keys_to_remove), outer_callback);
}
void StrikeDatabase::GetProtoStrikeData(const std::string key,
const GetValueCallback& callback) {
if (!database_initialized_) {
callback.Run(false, nullptr);
return;
}
db_->GetEntry(key, callback);
}
void StrikeDatabase::SetProtoStrikeData(const std::string& key,
const StrikeData& data,
const SetValueCallback& callback) {
if (!database_initialized_) {
callback.Run(false);
return;
}
std::unique_ptr<StrikeDataProto::KeyEntryVector> entries(
new StrikeDataProto::KeyEntryVector());
entries->push_back(std::make_pair(key, data));
db_->UpdateEntries(
/*entries_to_save=*/std::move(entries),
/*keys_to_remove=*/std::make_unique<std::vector<std::string>>(),
callback);
}
void StrikeDatabase::OnGetProtoStrikes(
StrikesCallback callback,
bool success,
std::unique_ptr<StrikeData> strike_data) {
if (success && strike_data)
callback.Run(strike_data->num_strikes());
else
callback.Run(0);
}
void StrikeDatabase::LoadKeys(const LoadKeysCallback& callback) {
db_->LoadKeys(callback);
}
void StrikeDatabase::UpdateCache(const std::string& key,
const StrikeData& data) {
strike_map_cache_[key] = data;
}
} // namespace autofill