Commit a276a89f authored by Tao Bai's avatar Tao Bai Committed by Commit Bot

[WebView Autofill] Notify select control change

Bug: 785531
Change-Id: I2d9480537259606a347746ab7cdc81c40a938290
Reviewed-on: https://chromium-review.googlesource.com/957426Reviewed-by: default avatarSebastien Seguin-Gagnon <sebsg@chromium.org>
Reviewed-by: default avatarChangwan Ryu <changwan@chromium.org>
Commit-Queue: Tao Bai <michaelbai@chromium.org>
Cr-Commit-Position: refs/heads/master@{#542978}
parent dfeaf530
......@@ -270,14 +270,14 @@ public class AwAutofillProvider extends AutofillProvider {
}
@Override
public void onTextFieldDidChange(int index, float x, float y, float width, float height) {
public void onFormFieldDidChange(int index, float x, float y, float width, float height) {
// Check index inside short value?
if (mRequest == null) return;
short sIndex = (short) index;
FocusField focusField = mRequest.getFocusField();
if (focusField == null || sIndex != focusField.fieldIndex) {
onFocusChanged(true, index, x, y, width, height);
onFocusChangedImpl(true, index, x, y, width, height, true /*causedByValueChange*/);
} else {
// Currently there is no api to notify both value and position
// change, before the API is availabe, we still need to call
......@@ -333,6 +333,12 @@ public class AwAutofillProvider extends AutofillProvider {
@Override
public void onFocusChanged(
boolean focusOnForm, int focusField, float x, float y, float width, float height) {
onFocusChangedImpl(
focusOnForm, focusField, x, y, width, height, false /*causedByValueChange*/);
}
private void onFocusChangedImpl(boolean focusOnForm, int focusField, float x, float y,
float width, float height, boolean causedByValueChange) {
// Check focusField inside short value?
// FocusNoLongerOnForm is called after form submitted.
if (mRequest == null) return;
......@@ -353,7 +359,7 @@ public class AwAutofillProvider extends AutofillProvider {
mContainerView, mRequest.getVirtualId((short) focusField), absBound);
// The focus field value might not sync with platform's
// AutofillManager, just notify it value changed.
notifyVirtualValueChanged(focusField);
if (!causedByValueChange) notifyVirtualValueChanged(focusField);
mRequest.setFocusField(new FocusField((short) focusField, absBound));
} else {
if (prev == null) return;
......@@ -422,4 +428,11 @@ public class AwAutofillProvider extends AutofillProvider {
return new Rect(
(int) bounds.left, (int) bounds.top, (int) bounds.right, (int) bounds.bottom);
}
@VisibleForTesting
public void fireSelectControlDidChangeForTesting(
int selectControlIndex, String selectControlId, String[] options, int selectedOption) {
nativeFireSelectControlDidChangeForTesting(mNativeAutofillProvider, selectControlIndex,
selectControlId, options, selectedOption);
}
}
......@@ -3618,6 +3618,15 @@ public class AwContents implements SmartClipProvider {
return true;
}
//--------------------------------------------------------------------------------------------
// Methods which is only for testing
//--------------------------------------------------------------------------------------------
@VisibleForTesting
public AwAutofillProvider getAwAutofillProviderForTesting() {
return (AwAutofillProvider) mAutofillProvider;
}
//--------------------------------------------------------------------------------------------
// Native methods
//--------------------------------------------------------------------------------------------
......
......@@ -1076,6 +1076,79 @@ public class AwAutofillTest {
}
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSelectControlChangeNotification() throws Throwable {
int cnt = 0;
TestWebServer webServer = TestWebServer.start();
final String data = "<!DOCTYPE html>"
+ "<html>"
+ "<body>"
+ "<form action='a.html' name='formname' id='formid'>"
+ "<input type='text' id='text1' name='username'>"
+ "<select id='color' autofocus><option value='red'>red</option><option "
+ "value='blue' id='blue'>blue</option></select>"
+ "</form>"
+ "</body>"
+ "</html>";
try {
final String url = webServer.setResponse(FILE, data, null);
loadUrlSync(url);
executeJavaScriptAndWaitForResult("document.getElementById('text1').select();");
dispatchDownAndUpKeyEvents(KeyEvent.KEYCODE_A);
cnt += waitForCallbackAndVerifyTypes(cnt,
new Integer[] {AUTOFILL_CANCEL, AUTOFILL_VIEW_ENTERED, AUTOFILL_VIEW_EXITED,
AUTOFILL_VIEW_ENTERED, AUTOFILL_VALUE_CHANGED});
clearChangedValues();
mAwContents.getAwAutofillProviderForTesting().fireSelectControlDidChangeForTesting(
1, "color", new String[] {"red", "blue"}, 1);
cnt += waitForCallbackAndVerifyTypes(cnt,
new Integer[] {
AUTOFILL_VIEW_EXITED, AUTOFILL_VIEW_ENTERED, AUTOFILL_VALUE_CHANGED});
ArrayList<Pair<Integer, AutofillValue>> values = getChangedValues();
assertEquals(1, values.size());
assertTrue(values.get(0).second.isList());
assertEquals(1, values.get(0).second.getListValue());
} finally {
webServer.shutdown();
}
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSelectControlChangeStartAutofillSession() throws Throwable {
int cnt = 0;
TestWebServer webServer = TestWebServer.start();
final String data = "<!DOCTYPE html>"
+ "<html>"
+ "<body>"
+ "<form action='a.html' name='formname' id='formid'>"
+ "<input type='text' id='text1' name='username'>"
+ "<select id='color' autofocus><option value='red'>red</option><option "
+ "value='blue' id='blue'>blue</option></select>"
+ "</form>"
+ "</body>"
+ "</html>";
try {
final String url = webServer.setResponse(FILE, data, null);
loadUrlSync(url);
// Change select control first shall start autofill session.
mAwContents.getAwAutofillProviderForTesting().fireSelectControlDidChangeForTesting(
1, "color", new String[] {"red", "blue"}, 1);
cnt += waitForCallbackAndVerifyTypes(cnt,
new Integer[] {AUTOFILL_CANCEL, AUTOFILL_VIEW_ENTERED, AUTOFILL_VIEW_EXITED,
AUTOFILL_VIEW_ENTERED, AUTOFILL_VALUE_CHANGED});
ArrayList<Pair<Integer, AutofillValue>> values = getChangedValues();
assertEquals(1, values.size());
assertTrue(values.get(0).second.isList());
assertEquals(1, values.get(0).second.getListValue());
} finally {
webServer.shutdown();
}
}
private void loadUrlSync(String url) throws Exception {
mRule.loadUrlSync(
mTestContainerView.getAwContents(), mContentsClient.getOnPageFinishedHelper(), url);
......
......@@ -7,6 +7,7 @@
#include <memory>
#include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/memory/ptr_util.h"
#include "components/autofill/android/form_data_android.h"
......@@ -60,15 +61,24 @@ void AutofillProviderAndroid::OnQueryFormFieldAutofill(
DCHECK_CURRENTLY_ON(BrowserThread::UI);
id_ = id;
// Only start a new session when form or handler is changed, the change of
// handler indicates query from other frame and a new session is needed.
//
// Focus or field value change will also trigger the query, so it should be
// ignored if the form is same.
if (IsCurrentlyLinkedForm(form) && IsCurrentlyLinkedHandler(handler)) {
return;
}
if (ShouldStartNewSession(handler, form))
StartNewSession(handler, form, field, bounding_box);
}
bool AutofillProviderAndroid::ShouldStartNewSession(
AutofillHandlerProxy* handler,
const FormData& form) {
// Only start a new session when form or handler is changed, the change of
// handler indicates query from other frame and a new session is needed.
return !IsCurrentlyLinkedForm(form) || !IsCurrentlyLinkedHandler(handler);
}
void AutofillProviderAndroid::StartNewSession(AutofillHandlerProxy* handler,
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
if (obj.is_null())
......@@ -106,22 +116,7 @@ void AutofillProviderAndroid::OnTextFieldDidChange(
const FormFieldData& field,
const gfx::RectF& bounding_box,
const base::TimeTicks timestamp) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
size_t index;
if (!IsCurrentlyLinkedHandler(handler) || !IsCurrentlyLinkedForm(form) ||
!form_->GetSimilarFieldIndex(field, &index))
return;
form_->OnTextFieldDidChange(index, field.value);
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
if (obj.is_null())
return;
gfx::RectF transformed_bounding = ToClientAreaBound(bounding_box);
Java_AutofillProvider_onTextFieldDidChange(
env, obj, index, transformed_bounding.x(), transformed_bounding.y(),
transformed_bounding.width(), transformed_bounding.height());
FireFormFieldDidChanged(handler, form, field, bounding_box);
}
void AutofillProviderAndroid::OnTextFieldDidScroll(
......@@ -135,7 +130,7 @@ void AutofillProviderAndroid::OnTextFieldDidScroll(
!form_->GetSimilarFieldIndex(field, &index))
return;
form_->OnTextFieldDidChange(index, field.value);
form_->OnFormFieldDidChange(index, field.value);
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
if (obj.is_null())
......@@ -147,6 +142,16 @@ void AutofillProviderAndroid::OnTextFieldDidScroll(
transformed_bounding.width(), transformed_bounding.height());
}
void AutofillProviderAndroid::OnSelectControlDidChange(
AutofillHandlerProxy* handler,
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box) {
if (ShouldStartNewSession(handler, form))
StartNewSession(handler, form, field, bounding_box);
FireFormFieldDidChanged(handler, form, field, bounding_box);
}
void AutofillProviderAndroid::FireSuccessfulSubmission(
SubmissionSource source) {
JNIEnv* env = AttachCurrentThread();
......@@ -217,6 +222,29 @@ void AutofillProviderAndroid::OnFocusChanged(bool focus_on_form,
bounding_box.width(), bounding_box.height());
}
void AutofillProviderAndroid::FireFormFieldDidChanged(
AutofillHandlerProxy* handler,
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
size_t index;
if (!IsCurrentlyLinkedHandler(handler) || !IsCurrentlyLinkedForm(form) ||
!form_->GetSimilarFieldIndex(field, &index))
return;
form_->OnFormFieldDidChange(index, field.value);
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
if (obj.is_null())
return;
gfx::RectF transformed_bounding = ToClientAreaBound(bounding_box);
Java_AutofillProvider_onFormFieldDidChange(
env, obj, index, transformed_bounding.x(), transformed_bounding.y(),
transformed_bounding.width(), transformed_bounding.height());
}
void AutofillProviderAndroid::OnDidFillAutofillFormData(
AutofillHandlerProxy* handler,
const FormData& form,
......@@ -236,6 +264,7 @@ void AutofillProviderAndroid::OnDidFillAutofillFormData(
void AutofillProviderAndroid::OnFormsSeen(AutofillHandlerProxy* handler,
const std::vector<FormData>& forms,
const base::TimeTicks) {
handler_for_testing_ = handler->GetWeakPtr();
if (!check_submission_)
return;
......@@ -287,4 +316,38 @@ void AutofillProviderAndroid::Reset() {
check_submission_ = false;
}
void AutofillProviderAndroid::FireSelectControlDidChangeForTesting(
JNIEnv* env,
jobject jcaller,
jint index,
jstring id,
jobjectArray options,
jint selected_option) {
FormData form_data;
FormFieldData form_field_data;
AutofillHandlerProxy* handler = nullptr;
if (form_.get() == nullptr) {
// Build a fake form
for (int i = 0; i < index; i++)
form_data.fields.push_back(FormFieldData());
base::android::AppendJavaStringArrayToStringVector(
env, options, &(form_field_data.option_values));
form_field_data.option_contents = form_field_data.option_values;
form_field_data.id = base::android::ConvertJavaStringToUTF16(env, id);
form_data.fields.push_back(form_field_data);
handler = handler_for_testing_.get();
} else {
form_data = form_->form_for_testing();
form_field_data = form_data.fields[index];
handler = handler_.get();
}
DCHECK(form_field_data.option_values.size() != 0);
DCHECK(!handler);
form_field_data.value = base::android::ConvertJavaStringToUTF16(
env, static_cast<jstring>(
env->GetObjectArrayElement(options, selected_option)));
OnSelectControlDidChange(handler, form_data, form_field_data, gfx::RectF());
}
} // namespace autofill
......@@ -40,6 +40,10 @@ class AutofillProviderAndroid : public AutofillProvider {
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box) override;
void OnSelectControlDidChange(AutofillHandlerProxy* handler,
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box) override;
bool OnFormSubmitted(AutofillHandlerProxy* handler,
const FormData& form,
bool known_success,
......@@ -61,12 +65,22 @@ class AutofillProviderAndroid : public AutofillProvider {
// Methods called by Java.
void OnAutofillAvailable(JNIEnv* env, jobject jcaller, jobject form_data);
void FireSelectControlDidChangeForTesting(JNIEnv* env,
jobject jcaller,
jint index,
jstring id,
jobjectArray options,
jint selected_option);
private:
void FireSuccessfulSubmission(SubmissionSource source);
void OnFocusChanged(bool focus_on_form,
size_t index,
const gfx::RectF& bounding_box);
void FireFormFieldDidChanged(AutofillHandlerProxy* handler,
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box);
bool IsCurrentlyLinkedHandler(AutofillHandlerProxy* handler);
......@@ -74,6 +88,14 @@ class AutofillProviderAndroid : public AutofillProvider {
gfx::RectF ToClientAreaBound(const gfx::RectF& bounding_box);
bool ShouldStartNewSession(AutofillHandlerProxy* handler,
const FormData& form);
void StartNewSession(AutofillHandlerProxy* handler,
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box);
void Reset();
int32_t id_;
......@@ -85,6 +107,8 @@ class AutofillProviderAndroid : public AutofillProvider {
// Valid only if check_submission_ is true.
SubmissionSource pending_submission_source_;
base::WeakPtr<AutofillHandlerProxy> handler_for_testing_;
DISALLOW_COPY_AND_ASSIGN(AutofillProviderAndroid);
};
} // namespace autofill
......
......@@ -64,10 +64,10 @@ ScopedJavaLocalRef<jobject> FormDataAndroid::GetNextFormFieldData(
return fields_[index_++]->GetJavaPeer();
}
void FormDataAndroid::OnTextFieldDidChange(size_t index,
void FormDataAndroid::OnFormFieldDidChange(size_t index,
const base::string16& value) {
form_.fields[index].value = value;
fields_[index]->OnTextFieldDidChange(value);
fields_[index]->OnFormFieldDidChange(value);
}
bool FormDataAndroid::GetFieldIndex(const FormFieldData& field, size_t* index) {
......
......@@ -44,7 +44,9 @@ class FormDataAndroid {
// Invoked when form field which specified by |index| is charged to new
// |value|.
void OnTextFieldDidChange(size_t index, const base::string16& value);
void OnFormFieldDidChange(size_t index, const base::string16& value);
const FormData& form_for_testing() { return form_; }
private:
FormData form_;
......
......@@ -76,7 +76,7 @@ void FormFieldDataAndroid::GetValue() {
field_ptr_->is_autofilled = true;
}
void FormFieldDataAndroid::OnTextFieldDidChange(const base::string16& value) {
void FormFieldDataAndroid::OnFormFieldDidChange(const base::string16& value) {
field_ptr_->value = value;
field_ptr_->is_autofilled = false;
JNIEnv* env = AttachCurrentThread();
......
......@@ -20,7 +20,7 @@ class FormFieldDataAndroid {
base::android::ScopedJavaLocalRef<jobject> GetJavaPeer();
void GetValue();
void OnTextFieldDidChange(const base::string16& value);
void OnFormFieldDidChange(const base::string16& value);
private:
// Not owned.
......
......@@ -9,6 +9,7 @@ import android.view.ViewGroup;
import android.view.ViewStructure;
import android.view.autofill.AutofillValue;
import org.chromium.base.VisibleForTesting;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.content_public.browser.WebContents;
......@@ -76,7 +77,7 @@ public abstract class AutofillProvider {
FormData formData, int focus, float x, float y, float width, float height);
/**
* Invoked when text field's value is changed.
* Invoked when form field's value is changed.
*
* @param index index of field in current form.
* @param x the boundary of focus field.
......@@ -86,7 +87,7 @@ public abstract class AutofillProvider {
*
*/
@CalledByNative
protected abstract void onTextFieldDidChange(
protected abstract void onFormFieldDidChange(
int index, float x, float y, float width, float height);
/**
......@@ -149,4 +150,9 @@ public abstract class AutofillProvider {
private native void nativeOnAutofillAvailable(
long nativeAutofillProviderAndroid, FormData formData);
@VisibleForTesting
protected native void nativeFireSelectControlDidChangeForTesting(
long nativeAutofillProviderAndroid, int index, String selectControlId, String[] options,
int seletedOption);
}
......@@ -58,7 +58,9 @@ void AutofillHandlerProxy::OnFocusOnFormFieldImpl(
void AutofillHandlerProxy::OnSelectControlDidChangeImpl(
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box) {}
const gfx::RectF& bounding_box) {
provider_->OnSelectControlDidChange(this, form, field, bounding_box);
}
void AutofillHandlerProxy::OnFocusNoLongerOnForm() {
provider_->OnFocusNoLongerOnForm(this);
......
......@@ -41,6 +41,11 @@ class AutofillProvider {
const FormFieldData& field,
const gfx::RectF& bounding_box) = 0;
virtual void OnSelectControlDidChange(AutofillHandlerProxy* handler,
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box) = 0;
virtual bool OnFormSubmitted(AutofillHandlerProxy* handler,
const FormData& form,
bool known_success,
......
......@@ -26,6 +26,12 @@ void TestAutofillProvider::OnTextFieldDidScroll(
const FormFieldData& field,
const gfx::RectF& bounding_box) {}
void TestAutofillProvider::OnSelectControlDidChange(
AutofillHandlerProxy* handler,
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box) {}
bool TestAutofillProvider::OnFormSubmitted(AutofillHandlerProxy* handler,
const FormData& form,
bool known_success,
......
......@@ -28,6 +28,10 @@ class TestAutofillProvider : public AutofillProvider {
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box) override;
void OnSelectControlDidChange(AutofillHandlerProxy* handler,
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box) override;
bool OnFormSubmitted(AutofillHandlerProxy* handler,
const FormData& form,
bool known_success,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment