Commit 5e0db9ff authored by Vadym Doroshenko's avatar Vadym Doroshenko Committed by Commit Bot

Introduction FieldInfo table in LoginDatabase.

This table contains field identifiers and autofill types that corresponds
that were leaned from user actions. In the subsequent CLs this will be
used for storing information whether particular form is username form
for username first flow.

Bug: 959776
Change-Id: If13482fa719638f6552f0e942a65c4731cccf3a7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1865150
Commit-Queue: Vadym Doroshenko <dvadym@chromium.org>
Reviewed-by: default avatarJan Wilken Dörrie <jdoerrie@chromium.org>
Cr-Commit-Position: refs/heads/master@{#706923}
parent 2814d325
......@@ -78,6 +78,8 @@ jumbo_static_library("browser") {
"export/password_csv_writer.h",
"export/password_manager_exporter.cc",
"export/password_manager_exporter.h",
"field_info_table.cc",
"field_info_table.h",
"form_fetcher.h",
"form_fetcher_impl.cc",
"form_fetcher_impl.h",
......@@ -506,6 +508,7 @@ source_set("unit_tests") {
"export/csv_writer_unittest.cc",
"export/password_csv_writer_unittest.cc",
"export/password_manager_exporter_unittest.cc",
"field_info_table_unittest.cc",
"form_fetcher_impl_unittest.cc",
"form_saver_impl_unittest.cc",
"generation/password_generator_unittest.cc",
......
// Copyright 2019 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/password_manager/core/browser/field_info_table.h"
#include "components/password_manager/core/browser/sql_table_builder.h"
#include "sql/database.h"
#include "sql/statement.h"
namespace password_manager {
namespace {
constexpr char kFieldInfoTableName[] = "field_info";
// Represents columns of the FieldInfoTable. Used with SQL queries that use all
// the columns.
enum class FieldInfoTableColumn {
kFormSignature,
kFieldSignature,
kFieldType,
kCreateTime,
};
// Casts the field info table column enum to its integer value.
int GetColumnNumber(FieldInfoTableColumn column) {
return static_cast<int>(column);
}
// Teaches |builder| about the different DB schemes in different versions.
void InitializeFieldInfoBuilder(SQLTableBuilder* builder) {
// Version 0.
builder->AddColumnToUniqueKey("form_signature", "INTEGER NOT NULL");
builder->AddColumnToUniqueKey("field_signature", "INTEGER NOT NULL");
builder->AddColumn("field_type", "INTEGER NOT NULL");
builder->AddColumn("create_time", "INTEGER NOT NULL");
builder->AddIndex("field_info_index", {"form_signature", "field_signature"});
builder->SealVersion();
}
// Returns a FieldInfo vector from the SQL statement.
std::vector<FieldInfo> StatementToFieldInfo(sql::Statement* s) {
std::vector<FieldInfo> results;
while (s->Step()) {
results.emplace_back();
results.back().form_signature =
s->ColumnInt64(GetColumnNumber(FieldInfoTableColumn::kFormSignature));
results.back().field_signature =
s->ColumnInt(GetColumnNumber(FieldInfoTableColumn::kFieldSignature));
results.back().field_type = static_cast<autofill::ServerFieldType>(
s->ColumnInt(GetColumnNumber(FieldInfoTableColumn::kFieldType)));
results.back().create_time = base::Time::FromDeltaSinceWindowsEpoch(
(base::TimeDelta::FromMicroseconds(s->ColumnInt64(
GetColumnNumber(FieldInfoTableColumn::kCreateTime)))));
}
return results;
}
} // namespace
bool operator==(const FieldInfo& lhs, const FieldInfo& rhs) {
return lhs.form_signature == rhs.form_signature &&
lhs.field_signature == rhs.field_signature &&
lhs.field_type == rhs.field_type && lhs.create_time == rhs.create_time;
}
void FieldInfoTable::Init(sql::Database* db) {
db_ = db;
}
bool FieldInfoTable::CreateTableIfNecessary() {
if (db_->DoesTableExist(kFieldInfoTableName))
return true;
SQLTableBuilder builder(kFieldInfoTableName);
InitializeFieldInfoBuilder(&builder);
return builder.CreateTable(db_);
}
bool FieldInfoTable::AddRow(const FieldInfo& field) {
sql::Statement s(db_->GetCachedStatement(
SQL_FROM_HERE,
"INSERT OR IGNORE INTO field_info "
"(form_signature, field_signature, field_type, create_time) "
"VALUES (?, ?, ?, ?)"));
s.BindInt64(GetColumnNumber(FieldInfoTableColumn::kFormSignature),
field.form_signature);
s.BindInt(GetColumnNumber(FieldInfoTableColumn::kFieldSignature),
field.field_signature);
s.BindInt(GetColumnNumber(FieldInfoTableColumn::kFieldType),
field.field_type);
s.BindInt64(GetColumnNumber(FieldInfoTableColumn::kCreateTime),
field.create_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
return s.Run();
}
bool FieldInfoTable::RemoveRowsByTime(base::Time remove_begin,
base::Time remove_end) {
sql::Statement s(
db_->GetCachedStatement(SQL_FROM_HERE,
"DELETE FROM field_info WHERE "
"create_time >= ? AND create_time < ?"));
s.BindInt64(0, remove_begin.ToDeltaSinceWindowsEpoch().InMicroseconds());
s.BindInt64(1, remove_end.ToDeltaSinceWindowsEpoch().InMicroseconds());
return s.Run();
}
std::vector<FieldInfo> FieldInfoTable::GetAllRows() {
sql::Statement s(db_->GetCachedStatement(
SQL_FROM_HERE,
"SELECT form_signature, field_signature, field_type, create_time FROM "
"field_info"));
return StatementToFieldInfo(&s);
}
// Returns all FieldInfo from the database which have |form_signature|.
std::vector<FieldInfo> FieldInfoTable::GetAllRowsForFormSignature(
uint64_t form_signature) {
sql::Statement s(
db_->GetCachedStatement(SQL_FROM_HERE,
"SELECT form_signature, field_signature, "
"field_type, create_time FROM field_info "
"WHERE form_signature = ?"));
s.BindInt64(0, form_signature);
return StatementToFieldInfo(&s);
}
} // namespace password_manager
// Copyright 2019 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.
#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FIELD_INFO_TABLE_H_
#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FIELD_INFO_TABLE_H_
#include "base/macros.h"
#include "base/time/time.h"
#include "components/autofill/core/browser/field_types.h"
namespace sql {
class Database;
}
namespace password_manager {
struct FieldInfo {
uint64_t form_signature = 0u;
uint32_t field_signature = 0u;
autofill::ServerFieldType field_type = autofill::UNKNOWN_TYPE;
// The date when the record was created.
base::Time create_time;
};
bool operator==(const FieldInfo& lhs, const FieldInfo& rhs);
class FieldInfoTable {
public:
FieldInfoTable() = default;
~FieldInfoTable() = default;
// Initializes |db_|.
void Init(sql::Database* db);
// Creates the table if it doesn't exist. Returns true if the table already
// exists or was created successfully.
bool CreateTableIfNecessary();
// Adds information about the field. Returns true if the SQL completed
// successfully.
bool AddRow(const FieldInfo& field);
// Removes all records created between |remove_begin| inclusive and
// |remove_end| exclusive.
bool RemoveRowsByTime(base::Time remove_begin, base::Time remove_end);
// Returns all FieldInfo from the database.
std::vector<FieldInfo> GetAllRows();
// Returns all FieldInfo from the database which have |form_signature|.
std::vector<FieldInfo> GetAllRowsForFormSignature(uint64_t form_signature);
private:
sql::Database* db_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(FieldInfoTable);
};
} // namespace password_manager
#endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_FIELD_INFO_TABLE_H_
// Copyright 2019 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/password_manager/core/browser/field_info_table.h"
#include <vector>
#include "base/files/scoped_temp_dir.h"
#include "components/autofill/core/browser/field_types.h"
#include "sql/database.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using autofill::PASSWORD;
using autofill::SINGLE_USERNAME;
using autofill::USERNAME;
using base::Time;
using testing::ElementsAre;
namespace password_manager {
namespace {
class FieldInfoTableTest : public testing::Test {
protected:
void SetUp() override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
ReloadDatabase();
}
void ReloadDatabase() {
base::FilePath file = temp_dir_.GetPath().AppendASCII("TestDatabase");
db_ = std::make_unique<FieldInfoTable>();
connection_ = std::make_unique<sql::Database>();
connection_->set_exclusive_locking();
ASSERT_TRUE(connection_->Open(file));
db_->Init(connection_.get());
ASSERT_TRUE(db_->CreateTableIfNecessary());
test_data_.push_back({101u, 1u, USERNAME, Time::FromTimeT(1)});
test_data_.push_back({101u, 10u, PASSWORD, Time::FromTimeT(5)});
test_data_.push_back({102u, 1u, SINGLE_USERNAME, Time::FromTimeT(10)});
}
base::ScopedTempDir temp_dir_;
std::unique_ptr<sql::Database> connection_;
std::unique_ptr<FieldInfoTable> db_;
std::vector<FieldInfo> test_data_;
};
TEST_F(FieldInfoTableTest, AddRow) {
EXPECT_TRUE(db_->AddRow(test_data_[0]));
EXPECT_EQ(test_data_[0], db_->GetAllRows()[0]);
}
TEST_F(FieldInfoTableTest, Reload) {
EXPECT_TRUE(db_->AddRow(test_data_[0]));
ReloadDatabase();
EXPECT_EQ(test_data_[0], db_->GetAllRows()[0]);
}
TEST_F(FieldInfoTableTest, AddManyRow) {
for (const FieldInfo& field : test_data_)
EXPECT_TRUE(db_->AddRow(field));
EXPECT_EQ(test_data_, db_->GetAllRows());
}
TEST_F(FieldInfoTableTest, RemoveRowsByTime) {
for (const FieldInfo& field : test_data_)
EXPECT_TRUE(db_->AddRow(field));
db_->RemoveRowsByTime(Time::FromTimeT(1), Time::FromTimeT(10));
std::vector<FieldInfo> expected_rows = {test_data_[2]};
EXPECT_EQ(expected_rows, db_->GetAllRows());
}
TEST_F(FieldInfoTableTest, GetAllRowsForFormSignature) {
for (const FieldInfo& field : test_data_)
EXPECT_TRUE(db_->AddRow(field));
constexpr uint64_t kFirstFormSignature = 101u;
constexpr uint64_t kSecondFormSignature = 102u;
constexpr uint64_t kNotExistingSignature = 1001;
std::vector<FieldInfo> expected_rows = {test_data_[0], test_data_[1]};
EXPECT_EQ(expected_rows,
db_->GetAllRowsForFormSignature(kFirstFormSignature));
expected_rows = {test_data_[2]};
EXPECT_EQ(expected_rows,
db_->GetAllRowsForFormSignature(kSecondFormSignature));
EXPECT_TRUE(db_->GetAllRowsForFormSignature(kNotExistingSignature).empty());
}
} // namespace
} // namespace password_manager
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment