Commit ec35d9cb authored by Moe Ahmadi's avatar Moe Ahmadi Committed by Commit Bot

[AF][IOS] Only clear fields in the same section as the one initiating clear

Bug: 816941
Cq-Include-Trybots: luci.chromium.try:ios-simulator-full-configs;master.tryserver.chromium.mac:ios-simulator-cronet
Change-Id: I5fae03657bd3e05ed101b4b85c2904aa28b3611d
Reviewed-on: https://chromium-review.googlesource.com/1089857Reviewed-by: default avatarEugene But <eugenebut@chromium.org>
Reviewed-by: default avatarJohn Wu <jzw@chromium.org>
Reviewed-by: default avatarOlivier Robin <olivierrobin@chromium.org>
Commit-Queue: Moe Ahmadi <mahmadi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#565705}
parent d8e41f7a
...@@ -478,7 +478,8 @@ void GetFormAndField(autofill::FormData* form, ...@@ -478,7 +478,8 @@ void GetFormAndField(autofill::FormData* form,
value:base::SysNSStringToUTF16(suggestion.value)]; value:base::SysNSStringToUTF16(suggestion.value)];
} else if (suggestion.identifier == autofill::POPUP_ITEM_ID_CLEAR_FORM) { } else if (suggestion.identifier == autofill::POPUP_ITEM_ID_CLEAR_FORM) {
[jsAutofillManager_ [jsAutofillManager_
clearAutofilledFieldsForFormNamed:formName clearAutofilledFieldsForFormName:formName
fieldIdentifier:fieldIdentifier
completionHandler:suggestionHandledCompletion_]; completionHandler:suggestionHandledCompletion_];
suggestionHandledCompletion_ = nil; suggestionHandledCompletion_ = nil;
} else { } else {
...@@ -735,7 +736,10 @@ void GetFormAndField(autofill::FormData* form, ...@@ -735,7 +736,10 @@ void GetFormAndField(autofill::FormData* form,
if (field.value.empty() || !field.is_autofilled) if (field.value.empty() || !field.is_autofilled)
continue; continue;
fieldsData.SetKey(base::UTF16ToUTF8(field.id), base::Value(field.value)); base::Value fieldData(base::Value::Type::DICTIONARY);
fieldData.SetKey("value", base::Value(field.value));
fieldData.SetKey("section", base::Value(field.section));
fieldsData.SetKey(base::UTF16ToUTF8(field.id), std::move(fieldData));
} }
autofillData.SetKey("fields", std::move(fieldsData)); autofillData.SetKey("fields", std::move(fieldsData));
......
...@@ -13,9 +13,13 @@ ...@@ -13,9 +13,13 @@
@interface FakeJSAutofillManager : JsAutofillManager @interface FakeJSAutofillManager : JsAutofillManager
// The name of the form that was most recently passed to // The name of the form that was most recently passed to
// |clearAutofilledFieldsForFormNamed:completionHandler:|. // |clearAutofilledFieldsForFormName:fieldIdentifier:completionHandler:|.
@property(nonatomic, copy, readonly) NSString* lastClearedFormName; @property(nonatomic, copy, readonly) NSString* lastClearedFormName;
// The field identifier that was most recently passed to
// |clearAutofilledFieldsForFormName:fieldIdentifier:completionHandler:|.
@property(nonatomic, copy, readonly) NSString* lastClearedFieldIdentifier;
@end @end
#endif // COMPONENTS_AUTOFILL_IOS_BROWSER_FAKE_JS_AUTOFILL_MANAGER_H_ #endif // COMPONENTS_AUTOFILL_IOS_BROWSER_FAKE_JS_AUTOFILL_MANAGER_H_
...@@ -14,11 +14,15 @@ ...@@ -14,11 +14,15 @@
@implementation FakeJSAutofillManager @implementation FakeJSAutofillManager
@synthesize lastClearedFormName = _lastClearedFormName; @synthesize lastClearedFormName = _lastClearedFormName;
@synthesize lastClearedFieldIdentifier = _lastClearedFieldIdentifier;
- (void)clearAutofilledFieldsForFormNamed:(NSString*)formName - (void)clearAutofilledFieldsForFormName:(NSString*)formName
fieldIdentifier:(NSString*)fieldIdentifier
completionHandler:(ProceduralBlock)completionHandler { completionHandler:(ProceduralBlock)completionHandler {
web::WebThread::PostTask(web::WebThread::UI, FROM_HERE, base::BindOnce(^{ web::WebThread::PostTask(web::WebThread::UI, FROM_HERE, base::BindOnce(^{
_lastClearedFormName = [formName copy]; _lastClearedFormName = [formName copy];
_lastClearedFieldIdentifier =
[fieldIdentifier copy];
completionHandler(); completionHandler();
})); }));
} }
......
...@@ -41,9 +41,11 @@ ...@@ -41,9 +41,11 @@
// autofilled are not modified. Field contents are cleared, and Autofill flag // autofilled are not modified. Field contents are cleared, and Autofill flag
// and styling are removed. 'change' events are sent for fields whose contents // and styling are removed. 'change' events are sent for fields whose contents
// changed. // changed.
// |fieldIdentifier| identifies the field that initiated the clear action.
// |completionHandler| is called after the forms are filled. |completionHandler| // |completionHandler| is called after the forms are filled. |completionHandler|
// cannot be nil. // cannot be nil.
- (void)clearAutofilledFieldsForFormNamed:(NSString*)formName - (void)clearAutofilledFieldsForFormName:(NSString*)formName
fieldIdentifier:(NSString*)fieldIdentifier
completionHandler:(ProceduralBlock)completionHandler; completionHandler:(ProceduralBlock)completionHandler;
// Marks up the form with autofill field prediction data (diagnostic tool). // Marks up the form with autofill field prediction data (diagnostic tool).
......
...@@ -109,13 +109,18 @@ ...@@ -109,13 +109,18 @@
}]; }];
} }
- (void)clearAutofilledFieldsForFormNamed:(NSString*)formName - (void)clearAutofilledFieldsForFormName:(NSString*)formName
fieldIdentifier:(NSString*)fieldIdentifier
completionHandler:(ProceduralBlock)completionHandler { completionHandler:(ProceduralBlock)completionHandler {
DCHECK(completionHandler); DCHECK(completionHandler);
NSString* script = NSString* script = [NSString
[NSString stringWithFormat: stringWithFormat:@"__gCrWeb.autofill.clearAutofilledFields(%s, %s);",
@"__gCrWeb.autofill.clearAutofilledFields(%s);", base::GetQuotedJSONString(
base::GetQuotedJSONString([formName UTF8String]).c_str()]; base::SysNSStringToUTF8(formName))
.c_str(),
base::GetQuotedJSONString(
base::SysNSStringToUTF8(fieldIdentifier))
.c_str()];
[_receiver executeJavaScript:script [_receiver executeJavaScript:script
completionHandler:^(id, NSError*) { completionHandler:^(id, NSError*) {
completionHandler(); completionHandler();
......
...@@ -21,7 +21,7 @@ goog.provide('__crWeb.autofill'); ...@@ -21,7 +21,7 @@ goog.provide('__crWeb.autofill');
* The autofill data for a form. * The autofill data for a form.
* @typedef {{ * @typedef {{
* formName: string, * formName: string,
* fields: !Object<string, string>, * fields: !Object<string, !Object<string, string>>,
* }} * }}
*/ */
var FormData; var FormData;
...@@ -279,10 +279,10 @@ __gCrWeb.autofill['fillForm'] = function(data, forceFillFieldIdentifier) { ...@@ -279,10 +279,10 @@ __gCrWeb.autofill['fillForm'] = function(data, forceFillFieldIdentifier) {
if (__gCrWeb.fill.isCheckableElement(element)) if (__gCrWeb.fill.isCheckableElement(element))
continue; continue;
// Skip fields if autofill data is missing. // Skip fields for which autofill data is missing.
var fieldIdentifier = __gCrWeb.form.getFieldIdentifier(element); var fieldIdentifier = __gCrWeb.form.getFieldIdentifier(element);
var value = data.fields[fieldIdentifier]; var fieldData = data.fields[fieldIdentifier];
if (!value) if (!fieldData)
continue; continue;
// Skip non-empty fields unless: // Skip non-empty fields unless:
...@@ -302,15 +302,16 @@ __gCrWeb.autofill['fillForm'] = function(data, forceFillFieldIdentifier) { ...@@ -302,15 +302,16 @@ __gCrWeb.autofill['fillForm'] = function(data, forceFillFieldIdentifier) {
continue; continue;
} }
(function(_element, _value, _delay) { (function(_element, _value, _section, _delay) {
window.setTimeout(function() { window.setTimeout(function() {
__gCrWeb.fill.setInputElementValue(_value, _element, function() { __gCrWeb.fill.setInputElementValue(_value, _element, function() {
_element.setAttribute('chrome-autofilled', ''); _element.setAttribute('chrome-autofilled', '');
_element.isAutofilled = true; _element.isAutofilled = true;
_element.autofillSection = _section;
_element.addEventListener('input', controlElementInputListener_); _element.addEventListener('input', controlElementInputListener_);
}); });
}, _delay); }, _delay);
})(element, value, delay); })(element, fieldData.value, fieldData.section, delay);
} }
if (form) { if (form) {
...@@ -329,30 +330,45 @@ __gCrWeb.autofill['fillForm'] = function(data, forceFillFieldIdentifier) { ...@@ -329,30 +330,45 @@ __gCrWeb.autofill['fillForm'] = function(data, forceFillFieldIdentifier) {
} }
}; };
// TODO(crbug.com/816941): Clear should only clear the current section and not
// the whole form.
/** /**
* Clear autofilled fields of the specified form section. Fields that are not * Clear autofilled fields of the specified form section. Fields that are not
* currently autofilled are not modified. * currently autofilled or do not belong to the same section as that of the
* field with |fieldIdentifier| are not modified. If the field identified by
* |fieldIdentifier| cannot be found all autofilled form fields get cleared.
* Field contents are cleared, and Autofill flag and styling are removed. * Field contents are cleared, and Autofill flag and styling are removed.
* 'change' events are sent for fields whose contents changed. * 'change' events are sent for fields whose contents changed.
* Based on FormCache::ClearSectionWithElement(). * Based on FormCache::ClearSectionWithElement().
* *
* @param {string} formName Identifier for form element (from * @param {string} formName Identifier for form element (from
* getFormIdentifier). * getFormIdentifier).
* @param {string} fieldIdentifier Identifier for form field initiating the
* clear action.
*/ */
__gCrWeb.autofill['clearAutofilledFields'] = function(formName) { __gCrWeb.autofill['clearAutofilledFields'] = function(
formName, fieldIdentifier) {
var form = __gCrWeb.form.getFormElementFromIdentifier(formName); var form = __gCrWeb.form.getFormElementFromIdentifier(formName);
var controlElements = form ? var controlElements = form ?
__gCrWeb.form.getFormControlElements(form) : __gCrWeb.form.getFormControlElements(form) :
getUnownedAutofillableFormFieldElements_(document.all, /*fieldsets=*/[]); getUnownedAutofillableFormFieldElements_(document.all, /*fieldsets=*/[]);
var formField = null;
for (var i = 0; i < controlElements.length; ++i) {
if (__gCrWeb.form.getFieldIdentifier(controlElements[i]) ==
fieldIdentifier) {
formField = controlElements[i];
break;
}
}
for (var i = 0, delay = 0; i < controlElements.length; for (var i = 0, delay = 0; i < controlElements.length;
++i, delay += __gCrWeb.autofill.delayBetweenFieldFillingMs) { ++i, delay += __gCrWeb.autofill.delayBetweenFieldFillingMs) {
var element = controlElements[i]; var element = controlElements[i];
if (!element.isAutofilled || element.disabled) if (!element.isAutofilled || element.disabled)
continue; continue;
if (formField && formField.autofillSection != element.autofillSection)
continue;
var value = null; var value = null;
if (__gCrWeb.fill.isTextInput(element) || if (__gCrWeb.fill.isTextInput(element) ||
__gCrWeb.fill.isTextAreaElement(element)) { __gCrWeb.fill.isTextAreaElement(element)) {
......
...@@ -120,8 +120,10 @@ ...@@ -120,8 +120,10 @@
#pragma mark - Public Methods #pragma mark - Public Methods
- (void)clearFormWithName:(NSString*)formName - (void)clearFormWithName:(NSString*)formName
fieldIdentifier:(NSString*)fieldIdentifier
completionHandler:(nullable void (^)(void))completionHandler { completionHandler:(nullable void (^)(void))completionHandler {
[_JSAutofillManager clearAutofilledFieldsForFormNamed:formName [_JSAutofillManager clearAutofilledFieldsForFormName:formName
fieldIdentifier:fieldIdentifier
completionHandler:^{ completionHandler:^{
if (completionHandler) { if (completionHandler) {
completionHandler(); completionHandler();
......
...@@ -143,6 +143,7 @@ TEST_F(CWVAutofillControllerTest, FillSuggestion) { ...@@ -143,6 +143,7 @@ TEST_F(CWVAutofillControllerTest, FillSuggestion) {
TEST_F(CWVAutofillControllerTest, ClearForm) { TEST_F(CWVAutofillControllerTest, ClearForm) {
__block BOOL clear_form_completion_was_called = NO; __block BOOL clear_form_completion_was_called = NO;
[autofill_controller_ clearFormWithName:kTestFormName [autofill_controller_ clearFormWithName:kTestFormName
fieldIdentifier:kTestFieldIdentifier
completionHandler:^{ completionHandler:^{
clear_form_completion_was_called = YES; clear_form_completion_was_called = YES;
}]; }];
...@@ -152,6 +153,8 @@ TEST_F(CWVAutofillControllerTest, ClearForm) { ...@@ -152,6 +153,8 @@ TEST_F(CWVAutofillControllerTest, ClearForm) {
return clear_form_completion_was_called; return clear_form_completion_was_called;
})); }));
EXPECT_NSEQ(kTestFormName, js_autofill_manager_.lastClearedFormName); EXPECT_NSEQ(kTestFormName, js_autofill_manager_.lastClearedFormName);
EXPECT_NSEQ(kTestFieldIdentifier,
js_autofill_manager_.lastClearedFieldIdentifier);
} }
// Tests CWVAutofillController focus previous field. // Tests CWVAutofillController focus previous field.
......
...@@ -24,15 +24,22 @@ CWV_EXPORT ...@@ -24,15 +24,22 @@ CWV_EXPORT
- (instancetype)init NS_UNAVAILABLE; - (instancetype)init NS_UNAVAILABLE;
// Clears the html form element with the 'name' attribute equal to |formName|. // Clears the fields that belong to the same autofill section as the field
// No-op if no such form is found. // identified by |fieldIdentifier| in the form named |formName|.
// No-op if no such form can be found in the current page. If the field
// identified by |fieldIdentifier| cannot be found the entire form gets cleared.
// |fieldIdentifier| identifies the field that had focus. It is passed to
// CWVAutofillControllerDelegate and forwarded to this method.
// |completionHandler| will only be called on success. // |completionHandler| will only be called on success.
- (void)clearFormWithName:(NSString*)formName - (void)clearFormWithName:(NSString*)formName
fieldIdentifier:(NSString*)fieldIdentifier
completionHandler:(nullable void (^)(void))completionHandler; completionHandler:(nullable void (^)(void))completionHandler;
// For the field named |fieldName|, identified by |fieldIdentifier| in the form // For the field named |fieldName|, identified by |fieldIdentifier| in the form
// named |formName|, fetches suggestions that can be used to autofill. // named |formName|, fetches suggestions that can be used to autofill.
// No-op if no such form and field can be found in the current page. // No-op if no such form and field can be found in the current page.
// |fieldIdentifier| identifies the field that had focus. It is passed to
// CWVAutofillControllerDelegate and forwarded to this method.
// |completionHandler| will only be called on success. // |completionHandler| will only be called on success.
- (void)fetchSuggestionsForFormWithName:(NSString*)formName - (void)fetchSuggestionsForFormWithName:(NSString*)formName
fieldName:(NSString*)fieldName fieldName:(NSString*)fieldName
......
...@@ -64,11 +64,12 @@ ...@@ -64,11 +64,12 @@
for (CWVAutofillSuggestion* suggestion in suggestions) { for (CWVAutofillSuggestion* suggestion in suggestions) {
[alertController addAction:[self actionForSuggestion:suggestion]]; [alertController addAction:[self actionForSuggestion:suggestion]];
} }
UIAlertAction* clearAction = UIAlertAction* clearAction = [UIAlertAction
[UIAlertAction actionWithTitle:@"Clear" actionWithTitle:@"Clear"
style:UIAlertActionStyleDefault style:UIAlertActionStyleDefault
handler:^(UIAlertAction* _Nonnull action) { handler:^(UIAlertAction* _Nonnull action) {
[autofillController clearFormWithName:formName [autofillController clearFormWithName:formName
fieldIdentifier:fieldIdentifier
completionHandler:nil]; completionHandler:nil];
}]; }];
[alertController addAction:clearAction]; [alertController addAction:clearAction];
......
...@@ -199,7 +199,9 @@ TEST_F(WebViewAutofillTest, TestSuggestionFetchFillClear) { ...@@ -199,7 +199,9 @@ TEST_F(WebViewAutofillTest, TestSuggestionFetchFillClear) {
return [fetched_suggestion.value isEqualToString:filled_value]; return [fetched_suggestion.value isEqualToString:filled_value];
})); }));
ASSERT_NSEQ(nil, filled_error); ASSERT_NSEQ(nil, filled_error);
[autofill_controller_ clearFormWithName:kTestFormName completionHandler:nil]; [autofill_controller_ clearFormWithName:kTestFormName
fieldIdentifier:kTestFieldID
completionHandler:nil];
NSString* cleared_script = [NSString NSString* cleared_script = [NSString
stringWithFormat:@"document.getElementById('%@').value", kTestFieldID]; stringWithFormat:@"document.getElementById('%@').value", kTestFieldID];
__block NSError* cleared_error = nil; __block NSError* cleared_error = nil;
......
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