Commit 68d6fb15 authored by Roger McFarlane's avatar Roger McFarlane Committed by Commit Bot

[autofill] add plumbing for ARIA label and description

This CL integrates ARIA label and description parsing into the render/JS
to browser communication path. This allows the browser to have knowledge
of the ARIA label and description.

Bug: 896719
Change-Id: If5e8d9389f10beecae102080ebcde066aae6c80a
Reviewed-on: https://chromium-review.googlesource.com/c/1342673Reviewed-by: default avatarMoe Ahmadi <mahmadi@chromium.org>
Reviewed-by: default avatarVadym Doroshenko <dvadym@chromium.org>
Reviewed-by: default avatarSebastien Seguin-Gagnon <sebsg@chromium.org>
Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Reviewed-by: default avatarOlivier Robin <olivierrobin@chromium.org>
Commit-Queue: Roger McFarlane <rogerm@chromium.org>
Cr-Commit-Position: refs/heads/master@{#611564}
parent eb073480
......@@ -5840,5 +5840,87 @@ TEST_F(FormAutofillTest, WebFormElementNotFoundInForm) {
EXTRACT_NONE, &form, &field));
}
TEST_F(FormAutofillTest, AriaLabelAndDescription) {
LoadHTML(
"<form id='form'>"
" <div id='label'>aria label</div>"
" <div id='description'>aria description</div>"
" <input type='text' id='field0' aria-label='inline aria label'>"
" <input type='text' id='field1' aria-labelledby='label'>"
" <input type='text' id='field2' aria-describedby='description'>"
"</form>");
WebLocalFrame* frame = GetMainFrame();
ASSERT_NE(nullptr, frame);
WebFormElement web_form =
frame->GetDocument().GetElementById("form").To<WebFormElement>();
ASSERT_FALSE(web_form.IsNull());
WebFormControlElement control_element =
frame->GetDocument().GetElementById("field0").To<WebFormControlElement>();
ASSERT_FALSE(control_element.IsNull());
FormData form;
FormFieldData field;
EXPECT_TRUE(WebFormElementToFormData(web_form, control_element, nullptr,
EXTRACT_NONE, &form, &field));
const std::vector<FormFieldData>& fields = form.fields;
ASSERT_EQ(3U, fields.size());
// Field 0
EXPECT_EQ(ASCIIToUTF16("inline aria label"), fields[0].aria_label);
EXPECT_EQ(ASCIIToUTF16(""), fields[0].aria_description);
// Field 1
EXPECT_EQ(ASCIIToUTF16("aria label"), fields[1].aria_label);
EXPECT_EQ(ASCIIToUTF16(""), fields[1].aria_description);
// Field 2
EXPECT_EQ(ASCIIToUTF16(""), fields[2].aria_label);
EXPECT_EQ(ASCIIToUTF16("aria description"), fields[2].aria_description);
}
TEST_F(FormAutofillTest, AriaLabelAndDescription2) {
LoadHTML(
"<form id='form'>"
" <input type='text' id='field0' aria-label='inline aria label'>"
" <input type='text' id='field1' aria-labelledby='label'>"
" <input type='text' id='field2' aria-describedby='description'>"
"</form>"
" <div id='label'>aria label</div>"
" <div id='description'>aria description</div>");
WebLocalFrame* frame = GetMainFrame();
ASSERT_NE(nullptr, frame);
WebFormElement web_form =
frame->GetDocument().GetElementById("form").To<WebFormElement>();
ASSERT_FALSE(web_form.IsNull());
WebFormControlElement control_element =
frame->GetDocument().GetElementById("field0").To<WebFormControlElement>();
ASSERT_FALSE(control_element.IsNull());
FormData form;
FormFieldData field;
EXPECT_TRUE(WebFormElementToFormData(web_form, control_element, nullptr,
EXTRACT_NONE, &form, &field));
const std::vector<FormFieldData>& fields = form.fields;
ASSERT_EQ(3U, fields.size());
// Field 0
EXPECT_EQ(ASCIIToUTF16("inline aria label"), fields[0].aria_label);
EXPECT_EQ(ASCIIToUTF16(""), fields[0].aria_description);
// Field 1
EXPECT_EQ(ASCIIToUTF16("aria label"), fields[1].aria_label);
EXPECT_EQ(ASCIIToUTF16(""), fields[1].aria_description);
// Field 2
EXPECT_EQ(ASCIIToUTF16(""), fields[2].aria_label);
EXPECT_EQ(ASCIIToUTF16("aria description"), fields[2].aria_description);
}
} // namespace form_util
} // namespace autofill
......@@ -113,6 +113,8 @@ struct FormFieldData {
string autocomplete_attribute;
mojo_base.mojom.String16 placeholder;
mojo_base.mojom.String16 css_classes;
mojo_base.mojom.String16 aria_label;
mojo_base.mojom.String16 aria_description;
uint32 unique_renderer_id;
uint32 properties_mask;
......
......@@ -553,6 +553,12 @@ bool StructTraits<
if (!data.ReadCssClasses(&out->css_classes))
return false;
if (!data.ReadAriaLabel(&out->aria_label))
return false;
if (!data.ReadAriaDescription(&out->aria_description))
return false;
if (!data.ReadSection(&out->section))
return false;
......
......@@ -160,6 +160,15 @@ struct StructTraits<autofill::mojom::FormFieldDataDataView,
return r.css_classes;
}
static const base::string16& aria_label(const autofill::FormFieldData& r) {
return r.aria_label;
}
static const base::string16& aria_description(
const autofill::FormFieldData& r) {
return r.aria_description;
}
static uint32_t unique_renderer_id(const autofill::FormFieldData& r) {
return r.unique_renderer_id;
}
......
......@@ -342,6 +342,8 @@ TEST_F(AutofillTypeTraitsTestImpl, PassFormFieldData) {
input.autocomplete_attribute = "on";
input.placeholder = base::ASCIIToUTF16("placeholder");
input.css_classes = base::ASCIIToUTF16("class1");
input.aria_label = base::ASCIIToUTF16("aria label");
input.aria_description = base::ASCIIToUTF16("aria description");
input.max_length = 12345;
input.is_autofilled = true;
input.check_status = FormFieldData::CHECKED;
......
......@@ -1546,6 +1546,9 @@ void WebFormControlElementToFormField(
element.UniqueRendererFormControlId());
}
field->aria_label = GetAriaLabel(element.GetDocument(), element);
field->aria_description = GetAriaDescription(element.GetDocument(), element);
if (!IsAutofillableElement(element))
return;
......
......@@ -1925,6 +1925,9 @@ __gCrWeb.fill.webFormControlElementToFormField = function(
field['role'] = __gCrWeb.fill.ROLE_ATTRIBUTE_PRESENTATION;
}
field['aria_label'] = __gCrWeb.fill.getAriaLabel(element);
field['aria_description'] = __gCrWeb.fill.getAriaDescription(element);
if (!__gCrWeb.fill.isAutofillableElement(element)) {
return;
}
......
......@@ -1603,6 +1603,8 @@ TEST_F(AutofillControllerJsTest, ExtractForms) {
@"name" : @"TestForm",
@"fields" : @[
@{
@"aria_description" : @"",
@"aria_label" : @"",
@"name" : @"firstname",
@"name_attribute" : @"firstname",
@"id_attribute" : @"firstname",
......@@ -1616,6 +1618,8 @@ TEST_F(AutofillControllerJsTest, ExtractForms) {
@"label" : @"* First name:"
},
@{
@"aria_description" : @"",
@"aria_label" : @"",
@"name" : @"vehicle",
@"name_attribute" : @"vehicle",
@"id_attribute" : @"vehicle1",
......@@ -1628,6 +1632,8 @@ TEST_F(AutofillControllerJsTest, ExtractForms) {
@"label" : @"Bicycle"
},
@{
@"aria_description" : @"",
@"aria_label" : @"",
@"name" : @"vehicle",
@"name_attribute" : @"vehicle",
@"id_attribute" : @"vehicle2",
......@@ -1640,6 +1646,8 @@ TEST_F(AutofillControllerJsTest, ExtractForms) {
@"label" : @"Automobile"
},
@{
@"aria_description" : @"",
@"aria_label" : @"",
@"name" : @"vehicle",
@"name_attribute" : @"vehicle",
@"id_attribute" : @"vehicle3",
......@@ -1652,6 +1660,8 @@ TEST_F(AutofillControllerJsTest, ExtractForms) {
@"label" : @"Missile"
},
@{
@"aria_description" : @"",
@"aria_label" : @"",
@"name" : @"nameintableth",
@"name_attribute" : @"nameintableth",
@"id_attribute" : @"nameintableth",
......@@ -1665,6 +1675,8 @@ TEST_F(AutofillControllerJsTest, ExtractForms) {
@"label" : @"* First name:"
},
@{
@"aria_description" : @"",
@"aria_label" : @"",
@"name" : @"emailtableth",
@"name_attribute" : @"",
@"id_attribute" : @"emailtableth",
......@@ -1678,6 +1690,8 @@ TEST_F(AutofillControllerJsTest, ExtractForms) {
@"label" : @"Email:"
},
@{
@"aria_description" : @"",
@"aria_label" : @"",
@"name" : @"pwd",
@"name_attribute" : @"pwd",
@"id_attribute" : @"pwd",
......@@ -1692,6 +1706,8 @@ TEST_F(AutofillControllerJsTest, ExtractForms) {
@"label" : @"* Password:"
},
@{
@"aria_description" : @"",
@"aria_label" : @"",
@"name" : @"state",
@"name_attribute" : @"state",
@"id_attribute" : @"state",
......
......@@ -91,17 +91,24 @@ TEST_F(JsAutofillManagerTest, InitAndInject) {
// Tests forms extraction method
// (fetchFormsWithRequirements:minimumRequiredFieldsCount:completionHandler:).
TEST_F(JsAutofillManagerTest, ExtractForms) {
LoadHtml(
@"<html><body><form name='testform' method='post'>"
"<input type='text' id='firstname' name='firstname'/>"
"<input type='text' id='lastname' name='lastname'/>"
"<input type='email' id='email' name='email'/>"
"</form></body></html>");
LoadHtml(@"<html><body><form name='testform' method='post'>"
"<div id='div1'>Last Name</div>"
"<div id='div2'>Email Address</div>"
"<input type='text' id='firstname' name='firstname'/"
" aria-label='First Name'>"
"<input type='text' id='lastname' name='lastname'"
" aria-labelledby='div1'/>"
"<input type='email' id='email' name='email'"
" aria-describedby='div2'/>"
"</form>"
"</body></html>");
NSDictionary* expected = @{
@"name" : @"testform",
@"fields" : @[
@{
@"aria_description" : @"",
@"aria_label" : @"First Name",
@"name" : @"firstname",
@"name_attribute" : @"firstname",
@"id_attribute" : @"firstname",
......@@ -112,9 +119,103 @@ TEST_F(JsAutofillManagerTest, ExtractForms) {
@"is_checkable" : @false,
@"is_focusable" : @true,
@"value" : @"",
@"label" : @"First Name"
},
@{
@"aria_description" : @"",
@"aria_label" : @"Last Name",
@"name" : @"lastname",
@"name_attribute" : @"lastname",
@"id_attribute" : @"lastname",
@"identifier" : @"lastname",
@"form_control_type" : @"text",
@"max_length" : GetDefaultMaxLength(),
@"should_autocomplete" : @true,
@"is_checkable" : @false,
@"is_focusable" : @true,
@"value" : @"",
@"label" : @""
},
@{
@"aria_description" : @"Email Address",
@"aria_label" : @"",
@"name" : @"email",
@"name_attribute" : @"email",
@"id_attribute" : @"email",
@"identifier" : @"email",
@"form_control_type" : @"email",
@"max_length" : GetDefaultMaxLength(),
@"should_autocomplete" : @true,
@"is_checkable" : @false,
@"is_focusable" : @true,
@"value" : @"",
@"label" : @""
}
]
};
__block BOOL block_was_called = NO;
__block NSString* result;
[manager_
fetchFormsWithMinimumRequiredFieldsCount:
autofill::MinRequiredFieldsForHeuristics()
inFrame:web::GetMainWebFrame(web_state())
completionHandler:^(NSString* actualResult) {
block_was_called = YES;
result = [actualResult copy];
}];
base::test::ios::WaitUntilCondition(^bool() {
return block_was_called;
});
NSArray* resultArray = [NSJSONSerialization
JSONObjectWithData:[result dataUsingEncoding:NSUTF8StringEncoding]
options:0
error:nil];
EXPECT_NSNE(nil, resultArray);
NSDictionary* form = [resultArray firstObject];
[expected enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL* stop) {
EXPECT_NSEQ(form[key], obj);
}];
}
// Tests forms extraction method
// (fetchFormsWithRequirements:minimumRequiredFieldsCount:completionHandler:).
TEST_F(JsAutofillManagerTest, ExtractForms2) {
LoadHtml(@"<html><body><form name='testform' method='post'>"
"<input type='text' id='firstname' name='firstname'/"
" aria-label='First Name'>"
"<input type='text' id='lastname' name='lastname'"
" aria-labelledby='div1'/>"
"<input type='email' id='email' name='email'"
" aria-describedby='div2'/>"
"</form>"
"<div id='div1'>Last Name</div>"
"<div id='div2'>Email Address</div>"
"</body></html>");
NSDictionary* expected = @{
@"name" : @"testform",
@"fields" : @[
@{
@"aria_description" : @"",
@"aria_label" : @"First Name",
@"name" : @"firstname",
@"name_attribute" : @"firstname",
@"id_attribute" : @"firstname",
@"identifier" : @"firstname",
@"form_control_type" : @"text",
@"max_length" : GetDefaultMaxLength(),
@"should_autocomplete" : @true,
@"is_checkable" : @false,
@"is_focusable" : @true,
@"value" : @"",
@"label" : @"First Name"
},
@{
@"aria_description" : @"",
@"aria_label" : @"Last Name",
@"name" : @"lastname",
@"name_attribute" : @"lastname",
@"id_attribute" : @"lastname",
......@@ -128,6 +229,8 @@ TEST_F(JsAutofillManagerTest, ExtractForms) {
@"label" : @""
},
@{
@"aria_description" : @"Email Address",
@"aria_label" : @"",
@"name" : @"email",
@"name_attribute" : @"email",
@"id_attribute" : @"email",
......
......@@ -148,12 +148,14 @@ TEST_F(PasswordControllerJsTest,
@"chromium.test/generic_submit\",\"name_attribute\":\"login_form\","
@"\"id_attribute\":\"\",\"fields\":[{\"identifier\":\"name\","
@"\"name\":\"name\",\"name_attribute\":\"name\",\"id_attribute\":"
@"\"\",\"form_control_type\":\"text\",\"should_autocomplete\":"
@"true,\"is_focusable\":true,\"max_length\":524288,\"is_checkable\":"
@"false,\"value\":\"\",\"label\":\"Name:\"},{\"identifier\":"
@"\"\",\"form_control_type\":\"text\",\"aria_label\":\"\","
@"\"aria_description\":\"\",\"should_autocomplete\":true,"
@"\"is_focusable\":true,\"max_length\":524288,\"is_checkable\":false,"
@"\"value\":\"\",\"label\":\"Name:\"},{\"identifier\":"
@"\"password\",\"name\":\"password\",\"name_attribute\":\"password\","
@"\"id_attribute\":\"\",\"form_control_type\":"
@"\"password\",\"should_autocomplete\":true,\"is_focusable\":true,"
@"\"id_attribute\":\"\",\"form_control_type\":\"password\","
@"\"aria_label\":\"\",\"aria_description\":\"\","
@"\"should_autocomplete\":true,\"is_focusable\":true,"
@"\"max_length\":524288,\"is_checkable\":false,\"value\":\"\","
@"\"label\":\"Password:\"}]}]",
base_url.c_str()];
......@@ -185,25 +187,30 @@ TEST_F(PasswordControllerJsTest,
@"generic_submit\",\"name_attribute\":\"\",\"id_attribute\":"
@"\"login_form1\",\"fields\":[{\"identifier\":\"name\","
@"\"name\":\"name\",\"name_attribute\":\"name\",\"id_attribute\":"
@"\"\",\"form_control_type\":\"text\",\"should_autocomplete\":"
@"\"\",\"form_control_type\":\"text\",\"aria_label\":\"\","
@"\"aria_description\":\"\",\"should_autocomplete\":"
@"true,\"is_focusable\":true,\"max_length\":524288,\"is_checkable\":"
@"false,\"value\":\"\",\"label\":\"Name:\"},{\"identifier\":"
@"\"password\",\"name\":\"password\",\"name_attribute\":\"password\","
@"\"id_attribute\":\"\",\"form_control_type\":"
@"\"password\",\"should_autocomplete\":true,\"is_focusable\":true,"
@"\"id_attribute\":\"\",\"form_control_type\":\"password\","
@"\"aria_label\":\"\",\"aria_description\":\"\","
@"\"should_autocomplete\":true,\"is_focusable\":true,"
@"\"max_length\":524288,\"is_checkable\":false,\"value\":\"\","
@"\"label\":\"Password:\"}]},{\"name\":\"login_form2\",\"origin\":"
@"\"https://chromium.test/\",\"action\":\"https://chromium.test/"
@"generic_s2\",\"name_attribute\":\"login_form2\","
@"\"id_attribute\":\"\",\"fields\":[{\"identifier\":\"name2\","
@"\"name\":\"name2\",\"name_attribute\":\"name2\",\"id_attribute\":"
@"\"\",\"form_control_type\":\"text\",\"should_autocomplete\":"
@"\"\",\"form_control_type\":\"text\",\"aria_label\":\"\","
@"\"aria_description\":\"\",\"should_autocomplete\":"
@"true,\"is_focusable\":true,\"max_length\":524288,\"is_checkable\":"
@"false,\"value\":\"\",\"label\":\"Name:\"},{\"identifier\":"
@"\"password2\",\"name\":\"password2\",\"name_attribute\":"
@"\"password2\",\"id_attribute\":\"\",\"form_control_type\":"
@"\"password\",\"should_autocomplete\":true,\"is_focusable\":true,"
@"\"max_length\":524288,\"is_checkable\":false,\"value\":\"\","
@"\"password\",\"aria_label\":\"\",\"aria_description\":\"\","
@"\"should_autocomplete\":true,\"is_focusable\":true,"
@"\"max_length\":524288,\"is_checkable\":false,"
@"\"value\":\"\","
@"\"label\":\"Password:\"}]}]",
base_url.c_str(), base_url.c_str()];
......@@ -231,12 +238,15 @@ TEST_F(PasswordControllerJsTest, GetPasswordFormData) {
@"\"name_attribute\":\"np\",\"id_attribute\":\"np1\","
@"\"fields\":[{\"identifier\":\"name\",\"name\":\"name\","
@"\"name_attribute\":\"name\",\"id_attribute\":\"\",\"form_"
@"control_type\":\"text\",\"should_autocomplete\":true,\"is_"
@"control_type\":\"text\",\"aria_label\":\"\","
@"\"aria_description\":\"\",\"should_autocomplete\":true,\"is_"
@"focusable\":true,\"max_length\":524288,\"is_checkable\":false,"
@"\"value\":\"\",\"label\":\"Name:\"},{\"identifier\":\"password\","
@"\"name\":\"password\",\"name_attribute\":\"password\","
@"\"id_attribute\":\"\",\"form_control_type\":\"password\",\"should_"
@"autocomplete\":true,\"is_focusable\":true,\"max_length\":524288,"
@"\"id_attribute\":\"\",\"form_control_type\":\"password\","
@"\"aria_label\":\"\",\"aria_description\":\"\","
@"\"should_autocomplete\":true,\"is_focusable\":true,"
@"\"max_length\":524288,"
@"\"is_checkable\":false,\"value\":\"\",\"label\":\"Password:\"}]}",
base_url.c_str(), base_url.c_str()];
......@@ -266,12 +276,15 @@ TEST_F(PasswordControllerJsTest, FormActionIsNotSet) {
@"\"name_attribute\":\"login_form\",\"id_attribute\":\"\","
@"\"fields\":[{\"identifier\":\"name\",\"name\":\"name\","
@"\"name_attribute\":\"name\",\"id_attribute\":\"\",\"form_"
@"control_type\":\"text\",\"should_autocomplete\":true,\"is_"
@"control_type\":\"text\",\"aria_label\":\"\","
@"\"aria_description\":\"\",\"should_autocomplete\":true,\"is_"
@"focusable\":true,\"max_length\":524288,\"is_checkable\":false,"
@"\"value\":\"\",\"label\":\"Name:\"},{\"identifier\":\"password\","
@"\"name\":\"password\",\"name_attribute\":\"password\","
@"\"id_attribute\":\"\",\"form_control_type\":\"password\",\"should_"
@"autocomplete\":true,\"is_focusable\":true,\"max_length\":524288,"
@"\"id_attribute\":\"\",\"form_control_type\":\"password\","
@"\"aria_label\":\"\",\"aria_description\":\"\","
@"\"should_autocomplete\":true,\"is_focusable\":true,"
@"\"max_length\":524288,"
@"\"is_checkable\":false,\"value\":\"\",\"label\":\"Password:\"}]}]",
base_url.c_str(), base_url.c_str()];
EXPECT_NSEQ(result, ExecuteJavaScriptWithFormat(
......@@ -321,12 +334,16 @@ TEST_F(PasswordControllerJsTest, TouchendAsSubmissionIndicator) {
@"\"id_attribute\":\"login_form\",\"fields\":"
@"[{\"identifier\":\"username\","
@"\"name\":\"username\",\"name_attribute\":\"username\","
@"\"id_attribute\":\"\",\"form_control_type\":\"text\",\"should_"
@"autocomplete\":true,\"is_focusable\":true,\"max_length\":524288,"
@"\"id_attribute\":\"\",\"form_control_type\":\"text\","
@"\"aria_label\":\"\",\"aria_description\":\"\","
@"\"should_autocomplete\":true,\"is_focusable\":true,"
@"\"max_length\":524288,"
@"\"is_checkable\":false,\"value\":\"user1\",\"label\":\"Name:\"},{"
@"\"identifier\":\"password\",\"name\":\"password\","
@"\"name_attribute\":\"password\",\"id_attribute\":\"\","
@"\"form_control_type\":\"password\",\"should_autocomplete\":true,"
@"\"form_control_type\":\"password\","
@"\"aria_label\":\"\",\"aria_description\":\"\","
@"\"should_autocomplete\":true,"
@"\"is_focusable\":true,\"max_length\":524288,\"is_checkable\":false,"
@"\"value\":\"password1\",\"label\":\"Password:\"}],"
@"\"command\":\"passwordForm.submitButtonClick\"}",
......
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