Commit 081912dd authored by Olivier Robin's avatar Olivier Robin Committed by Commit Bot

Autofill forms with pre-filled values

Monitor the source of the input and change events.
If the last value was set programmatically, allow to overwrite the value
by autofill.
This is the ios version of https://chromium-review.googlesource.com/c/chromium/src/+/1069226

Cq-Include-Trybots: luci.chromium.try:ios-simulator-full-configs;master.tryserver.chromium.mac:ios-simulator-cronet
Change-Id: I1b05f02bc93d9e77ce3de9fc939936766a659d87
Reviewed-on: https://chromium-review.googlesource.com/1092858
Commit-Queue: Olivier Robin <olivierrobin@chromium.org>
Reviewed-by: default avatarMoe Ahmadi <mahmadi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#565950}
parent 741f5154
......@@ -587,6 +587,10 @@ void GetFormAndField(autofill::FormData* form,
web::URLVerificationTrustLevel trustLevel;
const GURL pageURL(webState->GetCurrentURL(&trustLevel));
[jsAutofillManager_ toggleTrackingFormMutations:YES];
[jsAutofillManager_ toggleTrackingUserEditedFields:
base::FeatureList::IsEnabled(
autofill::features::kAutofillPrefilledFields)];
[self scanFormsInPage:webState pageURL:pageURL];
}
......
......@@ -57,6 +57,9 @@
// Toggles tracking form related changes in the page.
- (void)toggleTrackingFormMutations:(BOOL)state;
// Toggles tracking the source of the input events in the page.
- (void)toggleTrackingUserEditedFields:(BOOL)state;
// Designated initializer. |receiver| should not be nil.
- (instancetype)initWithReceiver:(CRWJSInjectionReceiver*)receiver
NS_DESIGNATED_INITIALIZER;
......
......@@ -91,6 +91,13 @@
[_receiver executeJavaScript:script completionHandler:nil];
}
- (void)toggleTrackingUserEditedFields:(BOOL)state {
NSString* script = [NSString
stringWithFormat:@"__gCrWeb.form.toggleTrackingUserEditedFields(%s);",
state ? "true" : "false"];
[_receiver executeJavaScript:script completionHandler:nil];
}
- (void)fillForm:(NSString*)dataString
forceFillFieldIdentifier:(NSString*)forceFillFieldIdentifier
completionHandler:(ProceduralBlock)completionHandler {
......
......@@ -290,7 +290,8 @@ __gCrWeb.autofill['fillForm'] = function(data, forceFillFieldIdentifier) {
// b) The element is a 'select-one' element. 'select-one' elements are
// always autofilled; see AutofillManager::FillOrPreviewDataModelForm().
// c) The "value" or "placeholder" attributes match the value, if any; or
if (element.value &&
// d) The value has not been set by the user.
if (element.value && __gCrWeb.form.fieldWasEditedByUser(element) &&
!__gCrWeb.autofill.sanitizedFieldIsEmpty(element.value) &&
fieldIdentifier !== forceFillFieldIdentifier &&
!__gCrWeb.fill.isSelectElement(element) &&
......
......@@ -68,6 +68,13 @@ __gCrWeb.form.messageToSend = null;
*/
__gCrWeb.form.lastFocusedElement = null;
/**
* A WeakMap to track if the current value of a field was entered by user or
* programmatically.
* If the map is null, the source of changed is not track.
*/
__gCrWeb.form.wasEditedByUser = null;
/**
* Based on Element::isFormControlElement() (WebKit)
* @param {Element} element A DOM element.
......@@ -336,6 +343,9 @@ var formActivity_ = function(evt) {
if (evt.type != 'blur') {
__gCrWeb.form.lastFocusedElement = document.activeElement;
}
if (['change', 'input'].includes(evt.type)) {
__gCrWeb.form.wasEditedByUser.set(target, evt.isTrusted);
}
if (target != __gCrWeb.form.lastFocusedElement) return;
var msg = {
'command': 'form.activity',
......@@ -400,16 +410,18 @@ var attachListeners_ = function() {
/**
* Focus events performed on the 'capture' phase otherwise they are often
* not received.
* Input and change performed on the 'capture' phase as they are needed to
* detect if the current value is entered by the user.
*/
document.addEventListener('focus', formActivity_, true);
document.addEventListener('blur', formActivity_, true);
document.addEventListener('change', formActivity_, true);
document.addEventListener('input', formActivity_, true);
/**
* Text input is watched at the bubbling phase as this seems adequate in
* Other events are watched at the bubbling phase as this seems adequate in
* practice and it is less obtrusive to page scripts than capture phase.
*/
document.addEventListener('input', formActivity_, false);
document.addEventListener('keyup', formActivity_, false);
document.addEventListener('submit',submitHandler_, false);
};
......@@ -475,6 +487,34 @@ __gCrWeb.form['trackFormMutations'] = function(delay) {
document, {childList: true, subtree: true});
};
/**
* Enables or disables the tracking of input event sources.
*/
__gCrWeb.form['toggleTrackingUserEditedFields'] = function(track) {
if (track) {
__gCrWeb.form.wasEditedByUser =
__gCrWeb.form.wasEditedByUser || new WeakMap();
} else {
__gCrWeb.form.wasEditedByUser = null;
}
}
/**
* Returns whether the last |input| or |change| event on |element| was triggered
* by a user action (was "trusted").
*/
__gCrWeb.form['fieldWasEditedByUser'] = function(element) {
if (__gCrWeb.form.wasEditedByUser === null) {
// Input event sources is not tracked.
// Return true to preserve previous behavior.
return true;
}
if (!__gCrWeb.form.wasEditedByUser.has(element)) {
return false;
}
return __gCrWeb.form.wasEditedByUser.get(element);
}
/** Flush the message queue. */
if (__gCrWeb.message) {
__gCrWeb.message.invokeQueues();
......
......@@ -285,6 +285,10 @@ const flags_ui::FeatureEntry kFeatureEntries[] = {
{"autofill-dynamic-forms", flag_descriptions::kAutofillDynamicFormsName,
flag_descriptions::kAutofillDynamicFormsDescription, flags_ui::kOsIos,
FEATURE_VALUE_TYPE(autofill::features::kAutofillDynamicForms)},
{"autofill-prefilled-fields",
flag_descriptions::kAutofillPrefilledFieldsName,
flag_descriptions::kAutofillPrefilledFieldsDescription, flags_ui::kOsIos,
FEATURE_VALUE_TYPE(autofill::features::kAutofillPrefilledFields)},
{"autofill-restrict-formless-form-extraction",
flag_descriptions::kAutofillRestrictUnownedFieldsToFormlessCheckoutName,
flag_descriptions::
......
......@@ -33,6 +33,10 @@ const char kAutofillDynamicFormsName[] = "Autofill dynamic forms";
const char kAutofillDynamicFormsDescription[] =
"Refills forms that dynamically change after an initial fill";
const char kAutofillPrefilledFieldsName[] = "Autofill prefilled forms";
const char kAutofillPrefilledFieldsDescription[] =
"Fills forms that contain a programmatically filled value.";
const char kAutofillEnforceMinRequiredFieldsForHeuristicsName[] =
"Autofill Enforce Min Required Fields For Heuristics";
const char kAutofillEnforceMinRequiredFieldsForHeuristicsDescription[] =
......
......@@ -24,6 +24,10 @@ extern const char kAutofillDownstreamUseGooglePayBrandingOniOSDescription[];
extern const char kAutofillDynamicFormsName[];
extern const char kAutofillDynamicFormsDescription[];
// Title and description for the flag to control the dynamic autofill.
extern const char kAutofillPrefilledFieldsName[];
extern const char kAutofillPrefilledFieldsDescription[];
// Enforcing restrictions to enable/disable autofill small form support.
extern const char kAutofillEnforceMinRequiredFieldsForHeuristicsName[];
extern const char kAutofillEnforceMinRequiredFieldsForHeuristicsDescription[];
......
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