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

Add metric of whether user change autofilled field

Bug: 831829
Change-Id: I328377efd74d64820a66ec4469b688847de43e50
Reviewed-on: https://chromium-review.googlesource.com/1045489Reviewed-by: default avatarTao Bai <michaelbai@chromium.org>
Reviewed-by: default avatarSteven Holte <holte@chromium.org>
Reviewed-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@{#557911}
parent 45d37513
......@@ -130,13 +130,13 @@ public class AwAutofillProvider extends AutofillProvider {
case FormFieldData.TYPE_LIST:
int j = value.getListValue();
if (j < 0 && j >= field.mOptionValues.length) continue;
field.updateValue(field.mOptionValues[j]);
field.setAutofillValue(field.mOptionValues[j]);
break;
case FormFieldData.TYPE_TOGGLE:
field.setChecked(value.getToggleValue());
break;
case FormFieldData.TYPE_TEXT:
field.updateValue((String) value.getTextValue());
field.setAutofillValue((String) value.getTextValue());
break;
default:
break;
......@@ -178,6 +178,10 @@ public class AwAutofillProvider extends AutofillProvider {
return toVirtualId(sessionId, index);
}
public FormFieldData getField(short index) {
return mFormData.mFields.get(index);
}
private static int findIndex(String[] values, String value) {
if (values != null && value != null) {
for (int i = 0; i < values.length; i++) {
......@@ -310,7 +314,7 @@ public class AwAutofillProvider extends AutofillProvider {
mRequest.setFocusField(new FocusField(focusField.fieldIndex, absBound));
}
notifyVirtualValueChanged(index);
mAutofillUMA.onUserChangeFieldValue();
mAutofillUMA.onUserChangeFieldValue(mRequest.getField(sIndex).hasPreviouslyAutofilled());
}
@Override
......
......@@ -34,14 +34,10 @@ public class AwAutofillUMA {
public static final int USER_NOT_SELECT_SUGGESTION_USER_NOT_CHANGE_FORM_NO_FORM_SUBMITTED = 13;
public static final int AUTOFILL_SESSION_HISTOGRAM_COUNT = 14;
// Records how user changed form, the enumerate values are the actions we concerned,
// not complete list, the rate could be figured out with UMA_AUTOFILL_WEBVIEW_AUTOFILL_SESSION.
public static final String UMA_AUTOFILL_WEBVIEW_USER_CHANGE_FORM =
"Autofill.WebView.UserChangeForm";
// The possible value of UMA_AUTOFILL_WEBVIEW_USER_CHANGE_FORM
public static final int USER_INPUT_BEFORE_SUGGESTION_DISPLAYED = 0;
public static final int USER_CHANGE_AUTOFILLED_FIELD = 1;
public static final int USER_CHANGE_FORM_COUNT = 2;
// Records whether user changed autofilled field if user ever changed the form. The action isn't
// recorded if user didn't change form at all.
public static final String UMA_AUTOFILL_USER_CHANGED_AUTOFILLED_FIELD =
"Autofill.WebView.UserChangedAutofilledField";
public static final String UMA_AUTOFILL_WEBVIEW_SUBMISSION_SOURCE =
"Autofill.WebView.SubmissionSource";
......@@ -66,27 +62,47 @@ public class AwAutofillUMA {
public static final int EVENT_VIRTUAL_STRUCTURE_PROVIDED = 0x1 << 0;
public static final int EVENT_SUGGESTION_DISPLAYED = 0x1 << 1;
public static final int EVENT_FORM_AUTOFILLED = 0x1 << 2;
public static final int EVENT_USER_CHANGE_FIELD_VALUE = 0x1 << 3;
public static final int EVENT_USER_CHANGED_FIELD_VALUE = 0x1 << 3;
public static final int EVENT_FORM_SUBMITTED = 0x1 << 4;
public static final int EVENT_USER_CHANGED_AUTOFILLED_FIELD = 0x1 << 5;
public void record(int event) {
// Not record any event until we get EVENT_VIRTUAL_STRUCTURE_PROVIDED which makes the
// following events meaningful.
if (event != EVENT_VIRTUAL_STRUCTURE_PROVIDED && mState == 0) return;
if (EVENT_USER_CHANGED_FIELD_VALUE == event && mUserChangedAutofilledField == null) {
mUserChangedAutofilledField = Boolean.valueOf(false);
} else if (EVENT_USER_CHANGED_AUTOFILLED_FIELD == event) {
if (mUserChangedAutofilledField == null) {
mUserChangedAutofilledField = Boolean.valueOf(true);
}
mUserChangedAutofilledField = true;
event = EVENT_USER_CHANGED_FIELD_VALUE;
}
mState |= event;
}
public int toUMAAutofillSessionValue() {
public void recordHistogram() {
RecordHistogram.recordEnumeratedHistogram(UMA_AUTOFILL_WEBVIEW_AUTOFILL_SESSION,
toUMAAutofillSessionValue(), AUTOFILL_SESSION_HISTOGRAM_COUNT);
// Only record if user ever changed form.
if (mUserChangedAutofilledField != null) {
RecordHistogram.recordBooleanHistogram(
UMA_AUTOFILL_USER_CHANGED_AUTOFILLED_FIELD, mUserChangedAutofilledField);
}
}
private int toUMAAutofillSessionValue() {
if (mState == 0)
return NO_CALLBACK_FORM_FRAMEWORK;
else if (mState == EVENT_VIRTUAL_STRUCTURE_PROVIDED)
return NO_SUGGESTION_USER_NOT_CHANGE_FORM_NO_FORM_SUBMITTED;
else if (mState == (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_USER_CHANGE_FIELD_VALUE))
else if (mState == (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_USER_CHANGED_FIELD_VALUE))
return NO_SUGGESTION_USER_CHANGE_FORM_NO_FORM_SUBMITTED;
else if (mState == (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_FORM_SUBMITTED))
return NO_SUGGESTION_USER_NOT_CHANGE_FORM_FORM_SUBMITTED;
else if (mState
== (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_USER_CHANGE_FIELD_VALUE
== (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_USER_CHANGED_FIELD_VALUE
| EVENT_FORM_SUBMITTED))
return NO_SUGGESTION_USER_CHANGE_FORM_FORM_SUBMITTED;
else if (mState
......@@ -99,12 +115,12 @@ public class AwAutofillUMA {
return USER_SELECT_SUGGESTION_USER_NOT_CHANGE_FORM_FORM_SUBMITTED;
else if (mState
== (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_SUGGESTION_DISPLAYED
| EVENT_FORM_AUTOFILLED | EVENT_USER_CHANGE_FIELD_VALUE
| EVENT_FORM_AUTOFILLED | EVENT_USER_CHANGED_FIELD_VALUE
| EVENT_FORM_SUBMITTED))
return USER_SELECT_SUGGESTION_USER_CHANGE_FORM_FORM_SUBMITTED;
else if (mState
== (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_SUGGESTION_DISPLAYED
| EVENT_FORM_AUTOFILLED | EVENT_USER_CHANGE_FIELD_VALUE))
| EVENT_FORM_AUTOFILLED | EVENT_USER_CHANGED_FIELD_VALUE))
return USER_SELECT_SUGGESTION_USER_CHANGE_FORM_NO_FORM_SUBMITTED;
else if (mState == (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_SUGGESTION_DISPLAYED))
return USER_NOT_SELECT_SUGGESTION_USER_NOT_CHANGE_FORM_NO_FORM_SUBMITTED;
......@@ -114,17 +130,18 @@ public class AwAutofillUMA {
return USER_NOT_SELECT_SUGGESTION_USER_NOT_CHANGE_FORM_FORM_SUBMITTED;
else if (mState
== (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_SUGGESTION_DISPLAYED
| EVENT_USER_CHANGE_FIELD_VALUE | EVENT_FORM_SUBMITTED))
| EVENT_USER_CHANGED_FIELD_VALUE | EVENT_FORM_SUBMITTED))
return USER_NOT_SELECT_SUGGESTION_USER_CHANGE_FORM_FORM_SUBMITTED;
else if (mState
== (EVENT_VIRTUAL_STRUCTURE_PROVIDED | EVENT_SUGGESTION_DISPLAYED
| EVENT_USER_CHANGE_FIELD_VALUE))
| EVENT_USER_CHANGED_FIELD_VALUE))
return USER_NOT_SELECT_SUGGESTION_USER_CHANGE_FORM_NO_FORM_SUBMITTED;
else
return SESSION_UNKNOWN;
}
private int mState;
private Boolean mUserChangedAutofilledField;
}
private SessionRecorder mRecorder;
......@@ -161,14 +178,17 @@ public class AwAutofillUMA {
if (mRecorder != null) mRecorder.record(SessionRecorder.EVENT_FORM_AUTOFILLED);
}
public void onUserChangeFieldValue() {
if (mRecorder != null) mRecorder.record(SessionRecorder.EVENT_USER_CHANGE_FIELD_VALUE);
public void onUserChangeFieldValue(boolean isPreviouslyAutofilled) {
if (mRecorder == null) return;
if (isPreviouslyAutofilled)
mRecorder.record(SessionRecorder.EVENT_USER_CHANGED_AUTOFILLED_FIELD);
else
mRecorder.record(SessionRecorder.EVENT_USER_CHANGED_FIELD_VALUE);
}
private void recordSession() {
if (mAutofillDisabled != null && !mAutofillDisabled.booleanValue() && mRecorder != null) {
RecordHistogram.recordEnumeratedHistogram(UMA_AUTOFILL_WEBVIEW_AUTOFILL_SESSION,
mRecorder.toUMAAutofillSessionValue(), AUTOFILL_SESSION_HISTOGRAM_COUNT);
mRecorder.recordHistogram();
}
mRecorder = null;
}
......
......@@ -585,6 +585,14 @@ public class AwAutofillTest {
mCnt, new Integer[] {AUTOFILL_VALUE_CHANGED});
}
public void simulateUserChangeAutofilledField() throws Throwable {
mTest.executeJavaScriptAndWaitForResult("document.getElementById('text1').select();");
mTest.dispatchDownAndUpKeyEvents(KeyEvent.KEYCODE_B);
mCnt += mTest.waitForCallbackAndVerifyTypes(mCnt,
new Integer[] {
AUTOFILL_VIEW_EXITED, AUTOFILL_VIEW_ENTERED, AUTOFILL_VALUE_CHANGED});
}
public void submitForm() throws Throwable {
mTest.executeJavaScriptAndWaitForResult("document.getElementById('formid').submit();");
mCnt += mTest.waitForCallbackAndVerifyTypes(
......@@ -628,9 +636,13 @@ public class AwAutofillTest {
i);
}
mAutofillWebViewViewEnabled = new MetricsUtils.HistogramDelta(
AwAutofillUMA.UMA_AUTOFILL_WEBVIEW_ENABLED, 1);
AwAutofillUMA.UMA_AUTOFILL_WEBVIEW_ENABLED, 1 /*true*/);
mAutofillWebViewViewDisabled = new MetricsUtils.HistogramDelta(
AwAutofillUMA.UMA_AUTOFILL_WEBVIEW_ENABLED, 0);
AwAutofillUMA.UMA_AUTOFILL_WEBVIEW_ENABLED, 0 /*false*/);
mUserChangedAutofilledField = new MetricsUtils.HistogramDelta(
AwAutofillUMA.UMA_AUTOFILL_USER_CHANGED_AUTOFILLED_FIELD, 1 /*true*/);
mUserChangedNonAutofilledField = new MetricsUtils.HistogramDelta(
AwAutofillUMA.UMA_AUTOFILL_USER_CHANGED_AUTOFILLED_FIELD, 0 /*falsTe*/);
}
});
}
......@@ -655,12 +667,46 @@ public class AwAutofillTest {
});
}
public void verifyUserChangedAutofilledField() throws Throwable {
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
assertEquals(0, mUserChangedNonAutofilledField.getDelta());
assertEquals(1, mUserChangedAutofilledField.getDelta());
}
});
}
public void verifyUserChangedNonAutofilledField() throws Throwable {
// User changed the form, but not the autofilled field.
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
assertEquals(1, mUserChangedNonAutofilledField.getDelta());
assertEquals(0, mUserChangedAutofilledField.getDelta());
}
});
}
public void verifyUserDidntChangeForm() throws Throwable {
// User didn't change the form at all.
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
assertEquals(0, mUserChangedNonAutofilledField.getDelta());
assertEquals(0, mUserChangedAutofilledField.getDelta());
}
});
}
private int mCnt = 0;
private AwAutofillTest mTest;
private volatile Integer mSessionValue;
private HashMap<MetricsUtils.HistogramDelta, Integer> mSessionDelta;
private MetricsUtils.HistogramDelta mAutofillWebViewViewEnabled;
private MetricsUtils.HistogramDelta mAutofillWebViewViewDisabled;
private MetricsUtils.HistogramDelta mUserChangedAutofilledField;
private MetricsUtils.HistogramDelta mUserChangedNonAutofilledField;
private volatile Integer mSourceValue;
private HashMap<MetricsUtils.HistogramDelta, Integer> mSubmissionSourceDelta;
}
......@@ -1501,6 +1547,7 @@ public class AwAutofillTest {
umaTestHelper.getSessionValue());
assertEquals(AwAutofillSessionUMATestHelper.NO_FORM_SUBMISSION,
umaTestHelper.getSubmissionSourceValue());
umaTestHelper.verifyUserChangedNonAutofilledField();
} finally {
webServer.shutdown();
}
......@@ -1520,6 +1567,7 @@ public class AwAutofillTest {
assertEquals(AwAutofillUMA.NO_SUGGESTION_USER_CHANGE_FORM_FORM_SUBMITTED,
umaTestHelper.getSessionValue());
assertEquals(AwAutofillUMA.FORM_SUBMISSION, umaTestHelper.getSubmissionSourceValue());
umaTestHelper.verifyUserChangedNonAutofilledField();
} finally {
webServer.shutdown();
}
......@@ -1540,6 +1588,7 @@ public class AwAutofillTest {
assertEquals(AwAutofillUMA.USER_SELECT_SUGGESTION_USER_NOT_CHANGE_FORM_FORM_SUBMITTED,
umaTestHelper.getSessionValue());
assertEquals(AwAutofillUMA.FORM_SUBMISSION, umaTestHelper.getSubmissionSourceValue());
umaTestHelper.verifyUserDidntChangeForm();
} finally {
webServer.shutdown();
}
......@@ -1562,6 +1611,7 @@ public class AwAutofillTest {
umaTestHelper.getSessionValue());
assertEquals(AwAutofillSessionUMATestHelper.NO_FORM_SUBMISSION,
umaTestHelper.getSubmissionSourceValue());
umaTestHelper.verifyUserDidntChangeForm();
} finally {
webServer.shutdown();
}
......@@ -1583,6 +1633,7 @@ public class AwAutofillTest {
umaTestHelper.getSessionValue());
assertEquals(AwAutofillSessionUMATestHelper.NO_FORM_SUBMISSION,
umaTestHelper.getSubmissionSourceValue());
umaTestHelper.verifyUserDidntChangeForm();
} finally {
webServer.shutdown();
}
......@@ -1603,6 +1654,7 @@ public class AwAutofillTest {
AwAutofillUMA.USER_NOT_SELECT_SUGGESTION_USER_NOT_CHANGE_FORM_FORM_SUBMITTED,
umaTestHelper.getSessionValue());
assertEquals(AwAutofillUMA.FORM_SUBMISSION, umaTestHelper.getSubmissionSourceValue());
umaTestHelper.verifyUserDidntChangeForm();
} finally {
webServer.shutdown();
}
......@@ -1622,6 +1674,7 @@ public class AwAutofillTest {
umaTestHelper.getSessionValue());
assertEquals(AwAutofillSessionUMATestHelper.NO_FORM_SUBMISSION,
umaTestHelper.getSubmissionSourceValue());
umaTestHelper.verifyUserDidntChangeForm();
} finally {
webServer.shutdown();
}
......@@ -1640,6 +1693,7 @@ public class AwAutofillTest {
assertEquals(AwAutofillUMA.NO_SUGGESTION_USER_NOT_CHANGE_FORM_FORM_SUBMITTED,
umaTestHelper.getSessionValue());
assertEquals(AwAutofillUMA.FORM_SUBMISSION, umaTestHelper.getSubmissionSourceValue());
umaTestHelper.verifyUserDidntChangeForm();
} finally {
webServer.shutdown();
}
......@@ -1695,6 +1749,28 @@ public class AwAutofillTest {
}
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testUMAUserChangeAutofilledField() throws Throwable {
TestWebServer webServer = TestWebServer.start();
try {
AwAutofillSessionUMATestHelper umaTestHelper = new AwAutofillSessionUMATestHelper(this);
umaTestHelper.triggerAutofill(webServer);
invokeOnProvideAutoFillVirtualStructure();
invokeOnInputUIShown();
umaTestHelper.simulateUserSelectSuggestion();
umaTestHelper.simulateUserChangeAutofilledField();
umaTestHelper.submitForm();
assertEquals(AwAutofillUMA.USER_SELECT_SUGGESTION_USER_CHANGE_FORM_FORM_SUBMITTED,
umaTestHelper.getSessionValue());
assertEquals(AwAutofillUMA.FORM_SUBMISSION, umaTestHelper.getSubmissionSourceValue());
umaTestHelper.verifyUserChangedAutofilledField();
} finally {
webServer.shutdown();
}
}
private void loadUrlSync(String url) throws Exception {
mRule.loadUrlSync(
mTestContainerView.getAwContents(), mContentsClient.getOnPageFinishedHelper(), url);
......
......@@ -41,6 +41,10 @@ public class FormFieldData {
private boolean mIsChecked;
private String mValue;
// Indicates whether mValue is autofilled.
private boolean mAutofilled;
// Indicates whether this fields was autofilled, but changed by user.
private boolean mPreviouslyAutofilled;
private FormFieldData(String name, String label, String value, String autocompleteAttr,
boolean shouldAutocomplete, String placeholder, String type, String id,
......@@ -79,9 +83,20 @@ public class FormFieldData {
return mValue;
}
public void setAutofillValue(String value) {
mValue = value;
updateAutofillState(true);
}
public void setChecked(boolean checked) {
mIsChecked = checked;
updateAutofillState(true);
}
@CalledByNative
public void updateValue(String value) {
private void updateValue(String value) {
mValue = value;
updateAutofillState(false);
}
@CalledByNative
......@@ -89,8 +104,13 @@ public class FormFieldData {
return mIsChecked;
}
public void setChecked(boolean checked) {
mIsChecked = checked;
public boolean hasPreviouslyAutofilled() {
return mPreviouslyAutofilled;
}
private void updateAutofillState(boolean autofilled) {
if (mAutofilled && !autofilled) mPreviouslyAutofilled = true;
mAutofilled = autofilled;
}
@CalledByNative
......
......@@ -6833,6 +6833,12 @@ http://cs/file:chrome/histograms.xml - but prefer this file for new entries.
<summary>Records the source of form submission.</summary>
</histogram>
<histogram name="Autofill.WebView.UserChangedAutofilledField"
enum="BooleanEnabled">
<owner>michaelbai@chromium.org</owner>
<summary>Whether the user changed autofilled field.</summary>
</histogram>
<histogram base="true" name="BackgroundFetch.EventDispatchFailure.Dispatch"
enum="ServiceWorkerStatusCode">
<!-- Name completed by histogram_suffixes name="BackgroundFetchEvents" -->
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