| // 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 <functional> |
| #include <memory> |
| #include <string_view> |
| #include <tuple> |
| |
| #include "base/containers/flat_map.h" |
| #include "base/types/cxx23_to_underlying.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_.fields) { |
| 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); |
| } |
| |
| FormDataAndroid::SimilarityCheckResult |
| FormDataAndroid::SimilarFormAsWithDiagnosis(const FormData& form) const { |
| SimilarityCheckResult result = kFormsAreSimilar; |
| |
| // Helper function that sets the `component` bit in `result` if the |
| // `projection` of `form_` and `form` differs. |
| auto check_component = [&](auto projection, |
| SimilarityCheckComponent component) { |
| if (std::invoke(projection, form_) != std::invoke(projection, form)) { |
| result.value() |= base::to_underlying(component); |
| } |
| }; |
| check_component(&FormData::global_id, SimilarityCheckComponent::kGlobalId); |
| check_component(&FormData::name, SimilarityCheckComponent::kName); |
| check_component(&FormData::id_attribute, |
| SimilarityCheckComponent::kIdAttribute); |
| check_component(&FormData::name_attribute, |
| SimilarityCheckComponent::kNameAttribute); |
| check_component(&FormData::url, SimilarityCheckComponent::kUrl); |
| check_component(&FormData::action, SimilarityCheckComponent::kAction); |
| |
| if (!SimilarFieldsAs(form)) { |
| result.value() |= base::to_underlying(SimilarityCheckComponent::kFields); |
| } |
| return result; |
| } |
| |
| 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 |