// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/android_autofill/browser/form_data_android.h"
#include <memory>
#include <string_view>
#include <tuple>
#include "base/containers/flat_map.h"
#include "components/android_autofill/browser/android_autofill_bridge_factory.h"
#include "components/android_autofill/browser/form_data_android_bridge.h"
#include "components/android_autofill/browser/form_field_data_android.h"
#include "components/autofill/core/browser/autofill_field.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/browser/form_structure.h"
#include "components/autofill/core/common/form_field_data.h"
#include "components/autofill/core/common/unique_ids.h"
namespace autofill {
FormDataAndroid::FormDataAndroid(const FormData& form, SessionId session_id)
: session_id_(session_id),
form_(form),
bridge_(AndroidAutofillBridgeFactory::GetInstance()
.CreateFormDataAndroidBridge()) {
fields_.reserve(form_.fields().size());
for (FormFieldData& field : form_.mutable_fields(/*pass_key=*/{})) {
fields_.push_back(std::make_unique<FormFieldDataAndroid>(&field));
}
}
FormDataAndroid::~FormDataAndroid() = default;
base::android::ScopedJavaLocalRef<jobject> FormDataAndroid::GetJavaPeer() {
return bridge_->GetOrCreateJavaPeer(form_, session_id_, fields_);
}
void FormDataAndroid::UpdateFromJava() {
for (std::unique_ptr<FormFieldDataAndroid>& field : fields_)
field->UpdateFromJava();
}
void FormDataAndroid::OnFormFieldDidChange(size_t index,
std::u16string_view value) {
fields_[index]->OnFormFieldDidChange(value);
}
bool FormDataAndroid::GetFieldIndex(const FormFieldData& field, size_t* index) {
for (size_t i = 0; i < form_.fields().size(); ++i) {
if (form_.fields()[i].SameFieldAs(field)) {
*index = i;
return true;
}
}
return false;
}
bool FormDataAndroid::GetSimilarFieldIndex(const FormFieldData& field,
size_t* index) {
for (size_t i = 0; i < form_.fields().size(); ++i) {
if (fields_[i]->SimilarFieldAs(field)) {
*index = i;
return true;
}
}
return false;
}
bool FormDataAndroid::SimilarFieldsAs(const FormData& form) const {
if (fields_.size() != form.fields().size()) {
return false;
}
for (size_t i = 0; i < fields_.size(); ++i) {
if (!fields_[i]->SimilarFieldAs(form.fields()[i])) {
return false;
}
}
return true;
}
bool FormDataAndroid::SimilarFormAs(const FormData& form) const {
// Note that comparing unique renderer ids alone is not a strict enough check,
// since these remain constant even if the page has dynamically modified its
// fields to have different labels, form control types, etc.
auto SimilarityTuple = [](const FormData& f) {
return std::tie(f.host_frame(), f.renderer_id(), f.name(), f.id_attribute(),
f.name_attribute(), f.url(), f.action());
};
return SimilarityTuple(form_) == SimilarityTuple(form) &&
SimilarFieldsAs(form);
}
void FormDataAndroid::UpdateFieldTypes(const FormStructure& form_structure) {
// Map FieldGlobalId's to their respective AutofillField, this way we can
// quickly ignore below FormFieldDataAndroid's with no matching AutofillField.
auto autofill_fields = base::MakeFlatMap<FieldGlobalId, const AutofillField*>(
form_structure, {}, [](const auto& field) {
return std::make_pair(field->global_id(), field.get());
});
for (auto& form_field_data_android : fields_) {
if (auto it = autofill_fields.find(form_field_data_android->global_id());
it != autofill_fields.end()) {
const AutofillField* autofill_field = it->second;
std::vector<AutofillType> server_predictions;
for (const auto& prediction : autofill_field->server_predictions()) {
server_predictions.emplace_back(
ToSafeFieldType(prediction.type(), NO_SERVER_DATA));
}
form_field_data_android->UpdateAutofillTypes(
FormFieldDataAndroid::FieldTypes(
AutofillType(autofill_field->heuristic_type()),
AutofillType(autofill_field->server_type()),
autofill_field->ComputedType(), std::move(server_predictions)));
}
}
}
void FormDataAndroid::UpdateFieldTypes(
const base::flat_map<FieldGlobalId, AutofillType>& types) {
for (const std::unique_ptr<FormFieldDataAndroid>& field : fields_) {
auto it = types.find(field->global_id());
if (it == types.end()) {
continue;
}
const AutofillType& new_type = it->second;
if (field->field_types() != new_type) {
field->UpdateAutofillTypes(FormFieldDataAndroid::FieldTypes(new_type));
}
}
}
std::vector<int> FormDataAndroid::UpdateFieldVisibilities(
const FormData& form) {
CHECK_EQ(form_.fields().size(), form.fields().size());
CHECK_EQ(form_.fields().size(), fields_.size());
// We rarely expect to find any difference in visibility - therefore do not
// reserve space in the vector.
std::vector<int> indices;
for (size_t i = 0; i < form_.fields().size(); ++i) {
if (form_.fields()[i].IsFocusable() != form.fields()[i].IsFocusable()) {
fields_[i]->OnFormFieldVisibilityDidChange(form.fields()[i]);
indices.push_back(i);
}
}
return indices;
}
} // namespace autofill