| // 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 "base/feature_list.h" |
| #include "components/android_autofill/browser/android_autofill_bridge_factory.h" |
| #include "components/android_autofill/browser/autofill_type_util.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/autofill_features.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 (base::FeatureList::IsEnabled( |
| features::kAutofillUseDeepEqualInsteadOfSameFieldAs) |
| ? FormFieldData::DeepEqual(form_.fields()[i], field) |
| : 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<FieldType> server_predictions; |
| for (const auto& prediction : autofill_field->server_predictions()) { |
| server_predictions.emplace_back( |
| ToSafeFieldType(prediction.type(), NO_SERVER_DATA)); |
| } |
| std::string_view overall_type = [&] { |
| if (HtmlFieldType html_field_type = autofill_field->html_type(); |
| html_field_type != HtmlFieldType::kUnspecified && |
| html_field_type != HtmlFieldType::kUnrecognized) { |
| return FieldTypeToStringView(html_field_type); |
| } |
| return FieldTypeToStringView( |
| GetMostRelevantFieldType(autofill_field->Type())); |
| }(); |
| form_field_data_android->UpdateFieldTypes( |
| FormFieldDataAndroid::FieldTypes( |
| autofill_field->heuristic_type(), autofill_field->server_type(), |
| overall_type, std::move(server_predictions))); |
| } |
| } |
| } |
| |
| void FormDataAndroid::UpdateFieldTypes( |
| const base::flat_map<FieldGlobalId, FieldType>& types) { |
| for (const std::unique_ptr<FormFieldDataAndroid>& field : fields_) { |
| auto it = types.find(field->global_id()); |
| if (it == types.end()) { |
| continue; |
| } |
| |
| FieldType new_type = it->second; |
| if (field->field_types() != new_type) { |
| field->UpdateFieldTypes(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 |