Commit 13fd76a0 authored by Roger McFarlane's avatar Roger McFarlane Committed by Commit Bot

[autofill] Add helpers to get the ARIA labels and descriptions

This CL adds the GetAriaLabel() and GetAriaDescription() helper functions
to the autofill::form_util namespace. These functions return the ARIA
label and description strings associated with an element.

The ARIA Label is the text referenced by one or more containing element
ids in the labelled elements aria-labelledby attribute, or the value
of the element's aria-label attribute, with preference given to the
aria-labelledby attribute. For example:

    <div id='foo-label'>Label for Foo</div>
    <input name='foo' aria-labelledby='foo-label'>
    <input name='bar' aria-label='Label for Bar'>

The aria-description is similar to aria-labelledby. For example:

    <div id='foo-description'>Long descriptive text for Foo</div>
    <input name='foo' aria-describedby='foo-descrition'>

Bug: 896719
Change-Id: Ida72c467d2e876b4d7a60ad56de56c7bac5489ec
Reviewed-on: https://chromium-review.googlesource.com/c/1325535
Commit-Queue: Roger McFarlane <rogerm@chromium.org>
Reviewed-by: default avatarSebastien Seguin-Gagnon <sebsg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#606936}
parent 4943b1d1
......@@ -19,6 +19,7 @@
#include "base/no_destructor.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
......@@ -2079,5 +2080,73 @@ std::vector<WebFormControlElement> FindFormControlElementsByUniqueRendererId(
return result;
}
namespace {
// Returns the coalesced child of the elements who's ids are founc in |id_list|.
//
// For example, given this document...
//
// <div id="billing">Billing</div>
// <div>
// <div id="name">Name</div>
// <input id="field1" type="text" aria-labelledby="billing name"/>
// </div>
// <div>
// <div id="address">Address</div>
// <input id="field2" type="text" aria-labelledby="billing address"/>
// </div>
//
// The coalesced text by the id_list found in the aria-labelledby attribute
// of the field1 input element would be "Billing Name" and for field2 it would
// be "Billing Address".
base::string16 CoalesceTextByIdList(const WebDocument& document,
const WebString& id_list) {
const base::string16 kSpace = base::ASCIIToUTF16(" ");
base::string16 text;
base::string16 id_list_utf16 = id_list.Utf16();
for (const auto& id : base::SplitStringPiece(
id_list_utf16, base::kWhitespaceUTF16, base::KEEP_WHITESPACE,
base::SPLIT_WANT_NONEMPTY)) {
auto node = document.GetElementById(WebString(id.data(), id.length()));
if (!node.IsNull()) {
base::string16 child_text = FindChildText(node);
if (!child_text.empty()) {
if (!text.empty())
text.append(kSpace);
text.append(child_text);
}
}
}
base::TrimWhitespace(text, base::TRIM_ALL, &text);
return text;
}
} // namespace
base::string16 GetAriaLabel(const blink::WebDocument& document,
const WebFormControlElement& element) {
static const base::NoDestructor<WebString> kAriaLabelledBy("aria-labelledby");
if (element.HasAttribute(*kAriaLabelledBy)) {
base::string16 text =
CoalesceTextByIdList(document, element.GetAttribute(*kAriaLabelledBy));
if (!text.empty())
return text;
}
static const base::NoDestructor<WebString> kAriaLabel("aria-label");
if (element.HasAttribute(*kAriaLabel))
return element.GetAttribute(*kAriaLabel).Utf16();
return base::string16();
}
base::string16 GetAriaDescription(const blink::WebDocument& document,
const WebFormControlElement& element) {
static const base::NoDestructor<WebString> kAriaDescribedBy(
"aria-describedby");
return CoalesceTextByIdList(document,
element.GetAttribute(*kAriaDescribedBy));
}
} // namespace form_util
} // namespace autofill
......@@ -316,6 +316,17 @@ FindFormControlElementsByUniqueRendererId(
uint32_t form_renderer_id,
const std::vector<uint32_t>& form_control_renderer_ids);
// Returns the ARIA label text of the elements denoted by the aria-labelledby
// attribute of |element| or the value of the aria-label attribute of
// |element|, with priority given to the aria-labelledby attribute.
base::string16 GetAriaLabel(const blink::WebDocument& document,
const blink::WebFormControlElement& element);
// Returns the ARIA label text of the elements denoted by the aria-describedby
// attribute of |element|.
base::string16 GetAriaDescription(const blink::WebDocument& document,
const blink::WebFormControlElement& element);
} // namespace form_util
} // namespace autofill
......
......@@ -477,3 +477,126 @@ TEST_F(FormAutofillUtilsTest, FindFormControlElementsByUniqueIdWithForm) {
EXPECT_TRUE(elements[1].IsNull());
EXPECT_TRUE(elements[2].IsNull());
}
// Tests the extraction of the aria-label attribute.
TEST_F(FormAutofillUtilsTest, GetAriaLabel) {
LoadHTML("<input id='input' type='text' aria-label='the label'/>");
WebDocument doc = GetMainFrame()->GetDocument();
auto element = doc.GetElementById("input").To<WebInputElement>();
EXPECT_EQ(autofill::form_util::GetAriaLabel(doc, element),
base::UTF8ToUTF16("the label"));
}
// Tests that aria-labelledby works. Simple case: only one id referenced.
TEST_F(FormAutofillUtilsTest, GetAriaLabelledBySingle) {
LoadHTML(
"<div id='billing'>Billing</div>"
"<div>"
" <div id='name'>Name</div>"
" <input id='input' type='text' aria-labelledby='name'/>"
"</div>");
WebDocument doc = GetMainFrame()->GetDocument();
auto element = doc.GetElementById("input").To<WebInputElement>();
EXPECT_EQ(autofill::form_util::GetAriaLabel(doc, element),
base::UTF8ToUTF16("Name"));
}
// Tests that aria-labelledby works: Complex case: multiple ids referenced.
TEST_F(FormAutofillUtilsTest, GetAriaLabelledByMulti) {
LoadHTML(
"<div id='billing'>Billing</div>"
"<div>"
" <div id='name'>Name</div>"
" <input id='input' type='text' aria-labelledby='billing name'/>"
"</div>");
WebDocument doc = GetMainFrame()->GetDocument();
auto element = doc.GetElementById("input").To<WebInputElement>();
EXPECT_EQ(autofill::form_util::GetAriaLabel(doc, element),
base::UTF8ToUTF16("Billing Name"));
}
// Tests that aria-labelledby takes precedence over aria-label
TEST_F(FormAutofillUtilsTest, GetAriaLabelledByTakesPrecedence) {
LoadHTML(
"<div id='billing'>Billing</div>"
"<div>"
" <div id='name'>Name</div>"
" <input id='input' type='text' aria-label='ignored' "
" aria-labelledby='name'/>"
"</div>");
WebDocument doc = GetMainFrame()->GetDocument();
auto element = doc.GetElementById("input").To<WebInputElement>();
EXPECT_EQ(autofill::form_util::GetAriaLabel(doc, element),
base::UTF8ToUTF16("Name"));
}
// Tests that an invalid aria-labelledby reference gets ignored (as opposed to
// crashing, for example).
TEST_F(FormAutofillUtilsTest, GetAriaLabelledByInvalid) {
LoadHTML(
"<div id='billing'>Billing</div>"
"<div>"
" <div id='name'>Name</div>"
" <input id='input' type='text' aria-labelledby='div1 div2'/>"
"</div>");
WebDocument doc = GetMainFrame()->GetDocument();
auto element = doc.GetElementById("input").To<WebInputElement>();
EXPECT_EQ(autofill::form_util::GetAriaLabel(doc, element),
base::UTF8ToUTF16(""));
}
// Tests that invalid aria-labelledby references fall back to aria-label.
TEST_F(FormAutofillUtilsTest, GetAriaLabelledByFallback) {
LoadHTML(
"<div id='billing'>Billing</div>"
"<div>"
" <div id='name'>Name</div>"
" <input id='input' type='text' aria-label='valid' "
" aria-labelledby='div1 div2'/>"
"</div>");
WebDocument doc = GetMainFrame()->GetDocument();
auto element = doc.GetElementById("input").To<WebInputElement>();
EXPECT_EQ(autofill::form_util::GetAriaLabel(doc, element),
base::UTF8ToUTF16("valid"));
}
// Tests that aria-describedby works: Simple case: a single id referenced.
TEST_F(FormAutofillUtilsTest, GetAriaDescribedBySingle) {
LoadHTML(
"<input id='input' type='text' aria-describedby='div1'/>"
"<div id='div1'>aria description</div>");
WebDocument doc = GetMainFrame()->GetDocument();
auto element = doc.GetElementById("input").To<WebInputElement>();
EXPECT_EQ(autofill::form_util::GetAriaDescription(doc, element),
base::UTF8ToUTF16("aria description"));
}
// Tests that aria-describedby works: Complex case: multiple ids referenced.
TEST_F(FormAutofillUtilsTest, GetAriaDescribedByMulti) {
LoadHTML(
"<input id='input' type='text' aria-describedby='div1 div2'/>"
"<div id='div2'>description</div>"
"<div id='div1'>aria</div>");
WebDocument doc = GetMainFrame()->GetDocument();
auto element = doc.GetElementById("input").To<WebInputElement>();
EXPECT_EQ(autofill::form_util::GetAriaDescription(doc, element),
base::UTF8ToUTF16("aria description"));
}
// Tests that invalid aria-describedby returns the empty string.
TEST_F(FormAutofillUtilsTest, GetAriaDescribedByInvalid) {
LoadHTML("<input id='input' type='text' aria-describedby='invalid'/>");
WebDocument doc = GetMainFrame()->GetDocument();
auto element = doc.GetElementById("input").To<WebInputElement>();
EXPECT_EQ(autofill::form_util::GetAriaDescription(doc, element),
base::UTF8ToUTF16(""));
}
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