chromium/components/android_autofill/browser/form_field_data_android_bridge_impl.cc

// Copyright 2023 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_field_data_android_bridge_impl.h"

#include <string>
#include <string_view>

#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/android/jni_weak_ref.h"
#include "base/android/scoped_java_ref.h"
#include "base/containers/span.h"
#include "components/autofill/core/browser/autofill_type.h"
#include "components/autofill/core/common/autofill_util.h"
#include "components/autofill/core/common/form_field_data.h"

// Must come after all headers that specialize FromJniType() / ToJniType().
#include "components/android_autofill/browser/jni_headers/FormFieldData_jni.h"

namespace autofill {

namespace {

using base::android::AttachCurrentThread;
using base::android::ConvertJavaStringToUTF16;
using base::android::ConvertUTF16ToJavaString;
using base::android::ConvertUTF8ToJavaString;
using base::android::ScopedJavaLocalRef;
using base::android::ToJavaArrayOfStrings;

// Converts the `AutofillType`s to strings and returns a Java array of strings.
// Returns `nullptr` instead if `server_predictions` is empty.
base::android::ScopedJavaLocalRef<jobjectArray> ToJavaArrayOfPredictionStrings(
    JNIEnv* env,
    const std::vector<AutofillType>& server_predictions) {
  if (!server_predictions.empty()) {
    std::vector<std::string> server_prediction_array;
    server_prediction_array.reserve(server_predictions.size());
    for (const auto& p : server_predictions) {
      server_prediction_array.emplace_back(std::string(p.ToStringView()));
    }
    return ToJavaArrayOfStrings(env, server_prediction_array);
  }
  return nullptr;
}

}  // namespace

FormFieldDataAndroidBridgeImpl::FormFieldDataAndroidBridgeImpl() = default;

FormFieldDataAndroidBridgeImpl::~FormFieldDataAndroidBridgeImpl() = default;

base::android::ScopedJavaLocalRef<jobject>
FormFieldDataAndroidBridgeImpl::GetOrCreateJavaPeer(
    const FormFieldData& field,
    const FormFieldDataAndroid::FieldTypes& field_types) {
  JNIEnv* env = AttachCurrentThread();
  if (ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); !obj.is_null()) {
    return obj;
  }

  auto ProjectOptions = [env](base::span<const SelectOption> options,
                              const auto& projection) {
    std::vector<std::u16string> projected_options;
    projected_options.reserve(options.size());
    base::ranges::transform(options, std::back_inserter(projected_options),
                            projection);
    return ToJavaArrayOfStrings(env, projected_options);
  };

  ScopedJavaLocalRef<jobject> obj = Java_FormFieldData_createFormFieldData(
      env, ConvertUTF16ToJavaString(env, field.name()),
      ConvertUTF16ToJavaString(env, field.label()),
      ConvertUTF16ToJavaString(env, field.value()),
      ConvertUTF8ToJavaString(env, field.autocomplete_attribute()),
      field.should_autocomplete(),
      ConvertUTF16ToJavaString(env, field.placeholder()),
      ConvertUTF8ToJavaString(
          env, FormControlTypeToString(field.form_control_type())),
      ConvertUTF16ToJavaString(env, field.id_attribute()),
      /*optionValues=*/ProjectOptions(field.options(), &SelectOption::value),
      /*optionContents=*/
      ProjectOptions(field.options(), &SelectOption::text),
      IsCheckable(field.check_status()), IsChecked(field.check_status()),
      field.max_length(),
      /*heuristicType=*/field_types.heuristic_type.IsUnknown()
          ? nullptr
          : ConvertUTF8ToJavaString(env,
                                    field_types.heuristic_type.ToStringView()),
      ConvertUTF8ToJavaString(env, field_types.server_type.ToStringView()),
      ConvertUTF8ToJavaString(env, field_types.computed_type.ToStringView()),
      ToJavaArrayOfPredictionStrings(env, field_types.server_predictions),
      field.bounds().x(), field.bounds().y(), field.bounds().right(),
      field.bounds().bottom(),
      /*datalistValues=*/
      ProjectOptions(field.datalist_options(), &SelectOption::value),
      /*datalistLabels=*/
      ProjectOptions(field.datalist_options(), &SelectOption::text),
      /*visible=*/field.IsFocusable(), field.is_autofilled());
  java_ref_ = JavaObjectWeakGlobalRef(env, obj);
  return obj;
}

void FormFieldDataAndroidBridgeImpl::UpdateFieldFromJava(FormFieldData& field) {
  JNIEnv* env = AttachCurrentThread();

  ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
  if (obj.is_null()) {
    return;
  }

  field.set_is_autofilled(Java_FormFieldData_isAutofilled(env, obj));
  if (IsCheckable(field.check_status())) {
    SetCheckStatus(&field, true, Java_FormFieldData_isChecked(env, obj));
    return;
  }
  if (ScopedJavaLocalRef<jstring> jvalue =
          Java_FormFieldData_getValue(env, obj);
      !jvalue.is_null()) {
    field.set_value(ConvertJavaStringToUTF16(env, jvalue));
  }
}

void FormFieldDataAndroidBridgeImpl::UpdateFieldTypes(
    const FormFieldDataAndroid::FieldTypes& field_types) {
  // TODO(crbug.com/40929724): Investigate why heuristic type is not updated.
  JNIEnv* env = AttachCurrentThread();
  ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
  if (obj.is_null()) {
    return;
  }

  Java_FormFieldData_updateFieldTypes(
      env, obj,
      ConvertUTF8ToJavaString(env, field_types.server_type.ToStringView()),
      ConvertUTF8ToJavaString(env, field_types.computed_type.ToStringView()),
      ToJavaArrayOfPredictionStrings(env, field_types.server_predictions));
}

void FormFieldDataAndroidBridgeImpl::UpdateValue(std::u16string_view value) {
  JNIEnv* env = AttachCurrentThread();
  ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
  if (obj.is_null()) {
    return;
  }

  Java_FormFieldData_updateValue(env, obj,
                                 ConvertUTF16ToJavaString(env, value));
}

void FormFieldDataAndroidBridgeImpl::UpdateVisible(bool visible) {
  JNIEnv* env = AttachCurrentThread();
  ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
  if (obj.is_null()) {
    return;
  }

  Java_FormFieldData_updateVisible(env, obj, visible);
}

}  // namespace autofill