// 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/android_autofill_provider_bridge_impl.h"
#include <string>
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/android/jni_weak_ref.h"
#include "base/containers/span.h"
#include "base/memory/raw_ref.h"
#include "components/android_autofill/browser/android_autofill_provider.h"
#include "components/android_autofill/browser/form_data_android.h"
#include "content/public/browser/web_contents.h"
#include "ui/gfx/geometry/rect_f.h"
// Must come after all headers that specialize FromJniType() / ToJniType().
#include "components/android_autofill/browser/jni_headers/AutofillProvider_jni.h"
namespace autofill {
using base::android::AttachCurrentThread;
using base::android::JavaParamRef;
using base::android::JavaRef;
using base::android::ScopedJavaLocalRef;
using base::android::ToJavaArrayOfStrings;
using base::android::ToJavaIntArray;
void JNI_AutofillProvider_Init(JNIEnv* env,
const JavaParamRef<jobject>& jcaller,
const JavaParamRef<jobject>& jweb_contents) {
auto* web_contents = content::WebContents::FromJavaWebContents(jweb_contents);
DCHECK(web_contents);
AndroidAutofillProvider::CreateForWebContents(web_contents);
AndroidAutofillProvider::FromWebContents(web_contents)
->AttachToJavaAutofillProvider(env, jcaller);
}
AndroidAutofillProviderBridgeImpl::AndroidAutofillProviderBridgeImpl(
Delegate* delegate)
: delegate_(*delegate) {}
AndroidAutofillProviderBridgeImpl::~AndroidAutofillProviderBridgeImpl() {
JNIEnv* env = AttachCurrentThread();
if (ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); !obj.is_null()) {
Java_AutofillProvider_setNativeAutofillProvider(env, obj, 0);
}
}
void AndroidAutofillProviderBridgeImpl::AttachToJavaAutofillProvider(
JNIEnv* env,
const JavaRef<jobject>& jcaller) {
DCHECK(java_ref_.get(env).is_null());
java_ref_ = JavaObjectWeakGlobalRef(env, jcaller);
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
if (obj.is_null()) {
return;
}
Java_AutofillProvider_setNativeAutofillProvider(
env, obj, reinterpret_cast<intptr_t>(this));
}
void AndroidAutofillProviderBridgeImpl::SendPrefillRequest(
FormDataAndroid& form) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
if (obj.is_null()) {
return;
}
Java_AutofillProvider_sendPrefillRequest(env, obj, form.GetJavaPeer());
}
void AndroidAutofillProviderBridgeImpl::StartAutofillSession(
FormDataAndroid& form,
const FieldInfo& field,
bool has_server_predictions) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
if (obj.is_null()) {
return;
}
Java_AutofillProvider_startAutofillSession(
env, obj, form.GetJavaPeer(), field.index, field.bounds.x(),
field.bounds.y(), field.bounds.width(), field.bounds.height(),
has_server_predictions);
}
void AndroidAutofillProviderBridgeImpl::OnServerPredictionsAvailable() {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
if (obj.is_null()) {
return;
}
Java_AutofillProvider_onServerPredictionsAvailable(env, obj);
}
void AndroidAutofillProviderBridgeImpl::OnFocusChanged(
const std::optional<FieldInfo>& field) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
if (obj.is_null()) {
return;
}
if (field) {
Java_AutofillProvider_onFocusChanged(
env, obj, /*focusOnForm=*/true, field->index, field->bounds.x(),
field->bounds.y(), field->bounds.width(), field->bounds.height());
} else {
Java_AutofillProvider_onFocusChanged(env, obj, /*focusOnForm=*/false, 0, 0,
0, 0, 0);
}
}
void AndroidAutofillProviderBridgeImpl::ShowDatalistPopup(
base::span<const SelectOption> options,
bool is_rtl) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
if (obj.is_null()) {
return;
}
std::vector<std::u16string> values;
std::vector<std::u16string> labels;
values.reserve(options.size());
labels.reserve(options.size());
for (const SelectOption& option : options) {
values.push_back(option.value);
labels.push_back(option.text);
}
Java_AutofillProvider_showDatalistPopup(
env, obj, ToJavaArrayOfStrings(env, values),
ToJavaArrayOfStrings(env, labels), is_rtl);
}
void AndroidAutofillProviderBridgeImpl::HideDatalistPopup() {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
if (obj.is_null()) {
return;
}
Java_AutofillProvider_hideDatalistPopup(env, obj);
}
void AndroidAutofillProviderBridgeImpl::OnTextFieldDidScroll(
const FieldInfo& field) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
if (obj.is_null()) {
return;
}
Java_AutofillProvider_onTextFieldDidScroll(
env, obj, field.index, field.bounds.x(), field.bounds.y(),
field.bounds.width(), field.bounds.height());
}
void AndroidAutofillProviderBridgeImpl::OnFormFieldDidChange(
const FieldInfo& field) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
if (obj.is_null()) {
return;
}
Java_AutofillProvider_onFormFieldDidChange(
env, obj, field.index, field.bounds.x(), field.bounds.y(),
field.bounds.width(), field.bounds.height());
}
void AndroidAutofillProviderBridgeImpl::OnFormFieldVisibilitiesDidChange(
base::span<const int> indices) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
if (obj.is_null()) {
return;
}
Java_AutofillProvider_onFormFieldVisibilitiesDidChange(
env, obj, ToJavaIntArray(env, indices));
}
void AndroidAutofillProviderBridgeImpl::OnFormSubmitted(
mojom::SubmissionSource submission_source) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
if (obj.is_null()) {
return;
}
Java_AutofillProvider_onFormSubmitted(env, obj,
static_cast<int>(submission_source));
}
void AndroidAutofillProviderBridgeImpl::OnDidFillAutofillFormData() {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
if (obj.is_null()) {
return;
}
Java_AutofillProvider_onDidFillAutofillFormData(env, obj);
}
void AndroidAutofillProviderBridgeImpl::CancelSession() {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
if (obj.is_null()) {
return;
}
Java_AutofillProvider_cancelSession(env, obj);
}
void AndroidAutofillProviderBridgeImpl::Reset() {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
if (obj.is_null()) {
return;
}
Java_AutofillProvider_reset(env, obj);
}
void AndroidAutofillProviderBridgeImpl::DetachFromJavaAutofillProvider(
JNIEnv* env) {
java_ref_.reset();
}
void AndroidAutofillProviderBridgeImpl::OnAutofillAvailable(JNIEnv* env) {
delegate_->OnAutofillAvailable();
}
void AndroidAutofillProviderBridgeImpl::OnAcceptDataListSuggestion(
JNIEnv* env,
std::u16string value) {
delegate_->OnAcceptDatalistSuggestion(value);
}
void AndroidAutofillProviderBridgeImpl::SetAnchorViewRect(JNIEnv* env,
jobject anchor_view,
jfloat x,
jfloat y,
jfloat width,
jfloat height) {
delegate_->SetAnchorViewRect(ScopedJavaLocalRef<jobject>(env, anchor_view),
gfx::RectF(x, y, width, height));
}
void AndroidAutofillProviderBridgeImpl::OnShowBottomSheetResult(
JNIEnv* env,
jboolean is_shown,
jboolean provided_autofill_structure) {
delegate_->OnShowBottomSheetResult(is_shown, provided_autofill_structure);
}
} // namespace autofill