0

ARIA 1.2 non-editable combobox

- Expose value, but not name, based on descendant text
- Expose correct role on Mac (popup button)

Bug: 1069150
Change-Id: I16119966a1e6c8a1eee88ef61c7268c5d3384341
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2142611
Commit-Queue: Aaron Leventhal <aleventhal@chromium.org>
Reviewed-by: Dominic Mazzoni <dmazzoni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#758007}
This commit is contained in:
Aaron Leventhal
2020-04-09 20:56:21 +00:00
committed by Commit Bot
parent ff7b2235b0
commit b5ac2bd6b7
20 changed files with 136 additions and 16 deletions

@ -268,6 +268,11 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest,
RunEventTest(FILE_PATH_LITERAL("aria-controls-changed.html"));
}
IN_PROC_BROWSER_TEST_P(DumpAccessibilityEventsTest,
AccessibilityEventsAriaComboBoxUneditable) {
RunEventTest(FILE_PATH_LITERAL("aria-combo-box-uneditable.html"));
}
#if defined(OS_WIN)
#define MAYBE_AccessibilityEventsAriaDisabledChanged \
DISABLED_AccessibilityEventsAriaDisabledChanged

@ -569,6 +569,11 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
RunAriaTest(FILE_PATH_LITERAL("aria-combobox.html"));
}
IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
AccessibilityAriaComboboxUneditable) {
RunAriaTest(FILE_PATH_LITERAL("aria-combobox-uneditable.html"));
}
#if defined(OS_ANDROID)
// TODO(crbug.com/986673): test is flaky on android.
#define MAYBE_AccessibilityAriaOnePointOneCombobox \

@ -0,0 +1,7 @@
android.webkit.WebView focusable focused scrollable
++android.view.View name='Choose a fruit, with text content'
++android.widget.Spinner clickable focusable name='Choose a fruit, with text content'
++android.widget.ListView role_description='list box' clickable collection item_count=3 row_count=3
++++android.view.View clickable collection_item focusable selected name='Apple'
++++android.view.View clickable collection_item focusable name='Banana' item_index=1 row_index=1
++++android.view.View clickable collection_item focusable name='Cherry' item_index=2 row_index=2

@ -0,0 +1,9 @@
[document web]
++[section] label-for
++++[static] name='Choose a fruit, with text content'
++[combo box] name='Choose a fruit, with text content' expandable controller-for labelled-by haspopup:listbox valuetext:Apple
++++[static] name='Apple'
++[list box] controlled-by
++++[list item] name='Apple' selectable selected
++++[list item] name='Banana' selectable
++++[list item] name='Cherry' selectable

@ -0,0 +1,13 @@
rootWebArea
++genericContainer ignored
++++genericContainer ignored
++++++genericContainer
++++++++staticText name='Choose a fruit, with text content'
++++++++++inlineTextBox name='Choose a fruit, with text content'
++++++comboBoxMenuButton collapsed name='Choose a fruit, with text content' value='Apple' haspopup=listbox controlsIds=listBox
++++++++staticText name='Apple'
++++++++++inlineTextBox name='Apple'
++++++listBox
++++++++listBoxOption name='Apple' selected=true
++++++++listBoxOption name='Banana' selected=false
++++++++listBoxOption name='Cherry' selected=false

@ -0,0 +1,9 @@
AXWebArea AXFocused='1'
++AXGroup
++++AXStaticText AXValue='Choose a fruit, with text content'
++AXPopUpButton AXTitle='Choose a fruit, with text content' AXValue='Apple' AXLinkedUIElements=["AXList"]
++++AXStaticText AXValue='Apple'
++AXList
++++AXStaticText AXValue='Apple'
++++AXStaticText AXValue='Banana'
++++AXStaticText AXValue='Cherry'

@ -0,0 +1,9 @@
Document
++Group IsControlElement=false
++++Text Name='Choose a fruit, with text content'
++ComboBox Name='Choose a fruit, with text content' ExpandCollapse.ExpandCollapseState='Collapsed' Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false Value.Value='Apple'
++++Text Name='Apple'
++List Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
++++ListItem Name='Apple' SelectionItem.IsSelected=true
++++ListItem Name='Banana' SelectionItem.IsSelected=false
++++ListItem Name='Cherry' SelectionItem.IsSelected=false

@ -0,0 +1,9 @@
ROLE_SYSTEM_DOCUMENT FOCUSED READONLY FOCUSABLE ia2_hypertext='<obj0><obj1><obj2>'
++IA2_ROLE_SECTION ia2_hypertext='Choose a fruit, with text content'
++++ROLE_SYSTEM_STATICTEXT name='Choose a fruit, with text content' ia2_hypertext='Choose a fruit, with text content'
++ROLE_SYSTEM_COMBOBOX name='Choose a fruit, with text content' value='Apple' COLLAPSED FOCUSABLE HASPOPUP haspopup:listbox ia2_hypertext='Apple'
++++ROLE_SYSTEM_STATICTEXT name='Apple' ia2_hypertext='Apple'
++ROLE_SYSTEM_LIST ia2_hypertext='<obj0><obj1><obj2>'
++++ROLE_SYSTEM_LISTITEM name='Apple' SELECTED FOCUSED FOCUSABLE SELECTABLE ia2_hypertext='Apple'
++++ROLE_SYSTEM_LISTITEM name='Banana' FOCUSABLE SELECTABLE ia2_hypertext='Banana'
++++ROLE_SYSTEM_LISTITEM name='Cherry' FOCUSABLE SELECTABLE ia2_hypertext='Cherry'

@ -0,0 +1,27 @@
<!DOCTYPE html>
<!--
@MAC-ALLOW:AXFocused='1'
@MAC-ALLOW:AXLinkedUIElements*
@MAC-DENY:AXLinkedUIElements=[]
@MAC-ALLOW:AXTitleUIElement*
@WIN-DENY:description*
@WIN-ALLOW:FOCUS*
@WIN-ALLOW:haspopup*
@WIN-ALLOW:ia2_hypertext=*
@WIN-ALLOW:SELECT*
@AURALINUX-ALLOW:collapsed*
@AURALINUX-ALLOW:expand*
@AURALINUX-ALLOW:haspopup:*
@AURALINUX-ALLOW:select*
@AURALINUX-ALLOW:value*
@BLINK-ALLOW:haspopup*
-->
<!-- Test a combobox box that acts like a <select size=1>. The value is computed like a name from descendants.
Note that some platforms do not call this a combobox, e.g. Mac calls this a popup button -->
<div id="combo1-label">Choose a fruit, with text content</div>
<div aria-controls="listbox1" aria-expanded="false" aria-haspopup="listbox" aria-labelledby="combo1-label" role="combobox" tabindex="0">Apple</div>
<div role="listbox" id="listbox1">
<div role="option" id="combo1-0" aria-selected="true">Apple</div>
<div role="option" id="combo1-1">Banana</div>
<div role="option" id="combo1-2">Cherry</div>
</div>

@ -1,10 +1,10 @@
AXWebArea
++AXButton AXHasPopup='1' AXHasPopupValue='menu'
++AXButton
++AXButton AXHasPopup='1' AXHasPopupValue='menu'
++AXButton AXHasPopup='1' AXHasPopupValue='listbox'
++AXButton AXHasPopup='1' AXHasPopupValue='grid'
++AXButton AXHasPopup='1' AXHasPopupValue='dialog'
++AXButton AXHasPopup='1' AXHasPopupValue='menu'
++AXButton AXHasPopup='1' AXHasPopupValue='listbox'
++AXButton AXHasPopup='1' AXHasPopupValue='listbox'
++AXPopUpButton AXHasPopup='1' AXHasPopupValue='menu'
++AXPopUpButton
++AXPopUpButton AXHasPopup='1' AXHasPopupValue='menu'
++AXPopUpButton AXHasPopup='1' AXHasPopupValue='listbox'
++AXPopUpButton AXHasPopup='1' AXHasPopupValue='grid'
++AXPopUpButton AXHasPopup='1' AXHasPopupValue='dialog'
++AXPopUpButton AXHasPopup='1' AXHasPopupValue='menu'
++AXPopUpButton AXHasPopup='1' AXHasPopupValue='listbox'
++AXPopUpButton AXHasPopup='1' AXHasPopupValue='listbox'

@ -31,7 +31,7 @@ rootWebArea
++++++++++++listBoxOption ignored name='3' selected=false
++++++genericContainer ignored
++++++++checkBox name='Test 5: Flash the screen two times.' checkedState=false
++++++++comboBoxMenuButton name='two'
++++++++comboBoxMenuButton value='two'
++++++++++listBox ignored
++++++++++++listBoxOption ignored name='1' selected=false
++++++++++++listBoxOption ignored name='two' selected=true

@ -0,0 +1 @@
AXValueChanged on AXPopUpButton AXValue="Orange"

@ -0,0 +1 @@
EVENT_OBJECT_VALUECHANGE on <div> role=ROLE_SYSTEM_COMBOBOX value="Orange" COLLAPSED,FOCUSABLE,HASPOPUP

@ -0,0 +1,12 @@
<!--
@WIN-DENY:*
@WIN-ALLOW:EVENT_OBJECT_VALUECHANGE*
-->
<div aria-controls="listbox1" aria-expanded="false" aria-haspopup="listbox" role="combobox" tabindex="0">Apple</div>
<div role="listbox" id="listbox1"><!-- Options would normally go here --></div>
<script>
function go() {
var combo_box = document.querySelector('[role=combobox]');
combo_box.innerText = 'Orange';
}
</script>

@ -1533,6 +1533,12 @@ String AXLayoutObject::StringValue() const {
}
}
// ARIA combobox can get value from inner contents.
if (AriaRoleAttribute() == ax::mojom::Role::kComboBoxMenuButton) {
AXObjectSet visited;
return TextFromDescendants(visited, false);
}
// FIXME: We might need to implement a value here for more types
// FIXME: It would be better not to advertise a value at all for the types for
// which we don't implement one; this would require subclassing or making

@ -2157,6 +2157,12 @@ String AXNodeObject::StringValue() const {
}
}
// ARIA combobox can get value from inner contents.
if (AriaRoleAttribute() == ax::mojom::Role::kComboBoxMenuButton) {
AXObjectSet visited;
return TextFromDescendants(visited, false);
}
return String();
}

@ -159,6 +159,8 @@ class MODULES_EXPORT AXNodeObject : public AXObject {
KURL Url() const override;
AXObject* ChooserPopup() const override;
String StringValue() const override;
String TextFromDescendants(AXObjectSet& visited,
bool recursive) const override;
// ARIA attributes.
ax::mojom::Role AriaRoleAttribute() const final;
@ -251,8 +253,6 @@ class MODULES_EXPORT AXNodeObject : public AXObject {
Member<Node> node_;
bool IsNativeCheckboxInMixedState() const;
String TextFromDescendants(AXObjectSet& visited,
bool recursive) const override;
String NativeTextAlternative(AXObjectSet& visited,
ax::mojom::NameFrom&,
AXRelatedObjectVector*,

@ -3587,7 +3587,6 @@ bool AXObject::NameFromContents(bool recursive) const {
case ax::mojom::Role::kCell:
case ax::mojom::Role::kCheckBox:
case ax::mojom::Role::kColumnHeader:
case ax::mojom::Role::kComboBoxMenuButton:
case ax::mojom::Role::kDocBackLink:
case ax::mojom::Role::kDocBiblioRef:
case ax::mojom::Role::kDocNoteRef:
@ -3598,7 +3597,6 @@ bool AXObject::NameFromContents(bool recursive) const {
case ax::mojom::Role::kLineBreak:
case ax::mojom::Role::kLink:
case ax::mojom::Role::kListBoxOption:
case ax::mojom::Role::kMenuButton:
case ax::mojom::Role::kMenuItem:
case ax::mojom::Role::kMenuItemCheckBox:
case ax::mojom::Role::kMenuItemRadio:
@ -3630,6 +3628,7 @@ bool AXObject::NameFromContents(bool recursive) const {
case ax::mojom::Role::kClient:
case ax::mojom::Role::kColorWell:
case ax::mojom::Role::kColumn:
case ax::mojom::Role::kComboBoxMenuButton: // Only value from content.
case ax::mojom::Role::kComboBoxGrouping:
case ax::mojom::Role::kComment:
case ax::mojom::Role::kComplementary:
@ -3699,6 +3698,7 @@ bool AXObject::NameFromContents(bool recursive) const {
case ax::mojom::Role::kMenuListPopup:
case ax::mojom::Role::kMenu:
case ax::mojom::Role::kMenuBar:
case ax::mojom::Role::kMenuButton: // Only value from content, not name.
case ax::mojom::Role::kMeter:
case ax::mojom::Role::kNavigation:
case ax::mojom::Role::kNote:

@ -1158,7 +1158,8 @@ void AXPlatformNodeBase::ComputeAttributes(PlatformAttributeList* attributes) {
}
// Expose slider value.
if (GetData().IsRangeValueSupported()) {
if (GetData().IsRangeValueSupported() ||
GetData().role == ax::mojom::Role::kComboBoxMenuButton) {
std::string value = base::UTF16ToUTF8(GetRangeValueText());
if (!value.empty())
AddAttributeToList("valuetext", value, attributes);

@ -56,7 +56,7 @@ RoleMap BuildRoleMap() {
{ax::mojom::Role::kColumn, NSAccessibilityColumnRole},
{ax::mojom::Role::kColumnHeader, @"AXCell"},
{ax::mojom::Role::kComboBoxGrouping, NSAccessibilityGroupRole},
{ax::mojom::Role::kComboBoxMenuButton, NSAccessibilityButtonRole},
{ax::mojom::Role::kComboBoxMenuButton, NSAccessibilityPopUpButtonRole},
{ax::mojom::Role::kComment, NSAccessibilityGroupRole},
{ax::mojom::Role::kComplementary, NSAccessibilityGroupRole},
{ax::mojom::Role::kContentDeletion, NSAccessibilityGroupRole},