Commit 837c2040 authored by dmazzoni's avatar dmazzoni Committed by Commit bot

Mac accessibility: only use AXTitleUIElement if that element has valid text.

On Mac OS X, the accessible name of an object can be exposed in three
different ways:

AXTitle: for visible text
AXDescription: for accessible text (like image "alt" or aria-label)
AXTitleUIElement: for controls labeled by another element

VoiceOver is kind of picky, it only wants us to expose just one of the
three, not multiple ones, but to be more confusing it ignores
AXTitleUIElement if it's not a control.

The bug here was that there was an obscure case where a checkbox's
name came from a label, but the label's text was "hidden" by a
heuristic that tries to combine the checkbox and its text into a
single element for simplicity. All of that is fine, but we were
exposing AXTitleUIElement for the checkbox when the element it
pointed to had empty text.

The fix is just to make sure the element pointed to by
AXTitleUIElement actually has text, otherwise we fall back on
AXTitle.

BUG=646846

Review-Url: https://codereview.chromium.org/2348663002
Cr-Commit-Position: refs/heads/master@{#419313}
parent ed437141
......@@ -772,24 +772,15 @@ NSString* const NSAccessibilityRequiredAttribute = @"AXRequired";
if ([self shouldExposeNameInAXValue])
return @"";
// If the name came from a single related element and it's present in the
// tree, it will be exposed in AXTitleUIElement.
std::vector<int32_t> labelledby_ids =
browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS);
// If we're exposing the title in TitleUIElement, don't also redundantly
// expose it in AXDescription.
if ([self shouldExposeTitleUIElement])
return @"";
ui::AXNameFrom nameFrom = static_cast<ui::AXNameFrom>(
browserAccessibility_->GetIntAttribute(ui::AX_ATTR_NAME_FROM));
std::string name = browserAccessibility_->GetStringAttribute(
ui::AX_ATTR_NAME);
// VoiceOver ignores titleUIElement on non-control AX nodes, so this special
// case expressly returns a nonempty text name for these nodes.
if (nameFrom == ui::AX_NAME_FROM_RELATED_ELEMENT &&
labelledby_ids.size() == 1 &&
browserAccessibility_->manager()->GetFromID(labelledby_ids[0]) &&
!browserAccessibility_->IsControl()) {
return base::SysUTF8ToNSString(name);
}
if (!name.empty()) {
// On Mac OS X, the accessible name of an object is exposed as its
// title if it comes from visible text, and as its description
......@@ -1237,6 +1228,35 @@ NSString* const NSAccessibilityRequiredAttribute = @"AXRequired";
}
}
// Returns true if this object should expose its accessible name using
// AXTitleUIElement rather than AXTitle or AXDescription. We only do
// this if it's a control, if there's a single label, and the label has
// nonempty text.
// internal
- (BOOL)shouldExposeTitleUIElement {
// VoiceOver ignores TitleUIElement if the element isn't a control.
if (!browserAccessibility_->IsControl())
return false;
ui::AXNameFrom nameFrom = static_cast<ui::AXNameFrom>(
browserAccessibility_->GetIntAttribute(ui::AX_ATTR_NAME_FROM));
if (nameFrom != ui::AX_NAME_FROM_RELATED_ELEMENT)
return false;
std::vector<int32_t> labelledby_ids =
browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS);
if (labelledby_ids.size() != 1)
return false;
BrowserAccessibility* label =
browserAccessibility_->manager()->GetFromID(labelledby_ids[0]);
if (!label)
return false;
std::string labelName = label->GetStringAttribute(ui::AX_ATTR_NAME);
return !labelName.empty();
}
// internal
- (content::BrowserAccessibilityDelegate*)delegate {
return [self instanceActive] ? browserAccessibility_->manager()->delegate()
......@@ -1716,21 +1736,16 @@ NSString* const NSAccessibilityRequiredAttribute = @"AXRequired";
if ([self shouldExposeNameInAXValue])
return @"";
// If the name came from a single related element and it's present in the
// tree, it will be exposed in AXTitleUIElement.
std::vector<int32_t> labelledby_ids =
browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS);
ui::AXNameFrom nameFrom = static_cast<ui::AXNameFrom>(
browserAccessibility_->GetIntAttribute(ui::AX_ATTR_NAME_FROM));
if (nameFrom == ui::AX_NAME_FROM_RELATED_ELEMENT &&
labelledby_ids.size() == 1 &&
browserAccessibility_->manager()->GetFromID(labelledby_ids[0])) {
// If we're exposing the title in TitleUIElement, don't also redundantly
// expose it in AXDescription.
if ([self shouldExposeTitleUIElement])
return @"";
}
// On Mac OS X, the accessible name of an object is exposed as its
// title if it comes from visible text, and as its description
// otherwise, but never both.
ui::AXNameFrom nameFrom = static_cast<ui::AXNameFrom>(
browserAccessibility_->GetIntAttribute(ui::AX_ATTR_NAME_FROM));
if (nameFrom == ui::AX_NAME_FROM_CONTENTS ||
nameFrom == ui::AX_NAME_FROM_RELATED_ELEMENT ||
nameFrom == ui::AX_NAME_FROM_VALUE) {
......@@ -1744,6 +1759,9 @@ NSString* const NSAccessibilityRequiredAttribute = @"AXRequired";
- (id)titleUIElement {
if (![self instanceActive])
return nil;
if (![self shouldExposeTitleUIElement])
return nil;
std::vector<int32_t> labelledby_ids =
browserAccessibility_->GetIntListAttribute(ui::AX_ATTR_LABELLEDBY_IDS);
ui::AXNameFrom nameFrom = static_cast<ui::AXNameFrom>(
......
......@@ -970,6 +970,11 @@ IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
RunHtmlTest(FILE_PATH_LITERAL("input-checkbox-in-menu.html"));
}
IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
AccessibilityInputCheckBoxLabel) {
RunHtmlTest(FILE_PATH_LITERAL("input-checkbox-label.html"));
}
IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityInputColor) {
RunHtmlTest(FILE_PATH_LITERAL("input-color.html"));
}
......
......@@ -19,7 +19,7 @@ AXWebArea AXRoleDescription='HTML content'
'
++++AXRadioButton AXRoleDescription='radio button' AXValue='0' AXARIASetSize='2' AXARIAPosInSet='2'
++++AXStaticText AXRoleDescription='text' AXValue='Banana'
++AXGroup AXRoleDescription='group' AXDescription='Cake' AXTitleUIElement='AXGroup'
++AXGroup AXRoleDescription='group' AXTitle='Cake'
++++AXGroup AXRoleDescription='group'
++++++AXStaticText AXRoleDescription='text' AXValue='Cake'
++++AXGroup AXRoleDescription='group'
......@@ -36,4 +36,4 @@ AXWebArea AXRoleDescription='HTML content'
'
++++++AXRadioButton AXRoleDescription='radio button' AXTitle='blue' AXValue='0' AXARIASetSize='1' AXARIAPosInSet='1'
++AXGroup AXRoleDescription='group'
++++AXStaticText AXRoleDescription='text' AXValue='Done'
\ No newline at end of file
++++AXStaticText AXRoleDescription='text' AXValue='Done'
AXWebArea
++AXTable AXDescription='Browser and Engine' AXTitleUIElement='AXGroup'
++AXTable AXTitle='Browser and Engine'
++++AXGroup
++++++AXStaticText AXValue='Browser and Engine'
++++AXRow
......
AXWebArea
++AXGroup
++++AXGroup AXDescription='Browser Engines:' AXTitleUIElement='AXGroup'
++++AXGroup AXTitle='Browser Engines:'
++++++AXGroup
++++++++AXStaticText AXValue='Browser Engines:'
AXWebArea
++AXGroup AXDescription='Fig.1 - A green Box' AXTitleUIElement='AXGroup'
++AXGroup AXTitle='Fig.1 - A green Box'
++++AXGroup
++++++AXImage AXDescription='This is a green box.'
++++AXGroup
......
AXWebArea
++AXGroup
++++AXGroup
++++++AXCheckBox AXTitle=' Checkbox Title' AXValue='0'
<html>
<body>
<label style="position: relative;">
<input type="checkbox">
Checkbox Title
</label>
</body>
</html>
AXWebArea
++AXGroup
++++AXGroup AXDescription='Browser Engines:' AXTitleUIElement='AXGroup'
++++AXGroup AXTitle='Browser Engines:'
++++++AXGroup
++++++++AXStaticText AXValue='Browser Engines:'
++++++AXGroup
......
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