Commit 0dae0ab0 authored by Jacques Newman's avatar Jacques Newman Committed by Commit Bot

Reland "Expose visibility-hidden elements"


This was reverted here:
https://chromium-review.googlesource.com/c/chromium/src/+/1692384
due to failure on failure on linux-chromeos-rel
ChromeVoxPanelTest.LinkMenu

As of https://chromium-review.googlesource.com/c/chromium/src/+/1708415
This test no longer fails.

Original CL:
https://chromium-review.googlesource.com/c/chromium/src/+/1646877

Original Change description:
> Expose visibility-hidden elements
>
> This change allows visibility-hidden elements to be returned from
> UIA_DescribedByPropertyId and UIA_LabeledByPropertyId. To allow for
> this, visibility-hidden elements had to be exposed to the AX tree.
> This change can be observed in the update to
> AXObject::ComputeAccessibilityIsIgnoredPassThrough().
>
> Given:
> <div id="a" aria-label="foo" style="visibility:hidden"> </div>
> <div id="b" aria-labelled-by="a"> </div>
>
> When doing TextAlternative calculation for b, we know we are
> "in_aria_labelled_by_traversal", therefore we do not check to see
> if the node "IsHiddenForTextAlternativeCalculation", therefore
> correctly calculating the name for node "b".
> But, when we are doing TextAlternative calculation for node "a",
> we do not know if node "a" is the target of aria-labelledby, and
> is not considered to be "in_aria_labelled_by_traversal", so the
> name is an empty string: "".
> This is an issue, as the UIA_LabeledByPropertyId on node "b"
> returns node "a", and node "a" has an incorrect name of empty
> string.
>
> For purposes of computing a non-recursive text alternative,
> if an ignored and IsHiddenForTextAlternativeCalculation node
> is included in the tree, assume that it is the target of
> aria-labelledby or aria-describedby, since we can't tell
> yet whether that's the case. If it isn't exposed, the AT
> will never see the name anyways.
>
> This new behavior is only applied when calculating the
> TextAlternative for the ignored node directly, not in the
> recursive case. This ensures this new behavior is only applied
> to the ignored node, and does not influence the name calculation
> of other nodes.

Bug: 651614
Change-Id: I6ec57cbd36606890eaf1fd4e72d8442cae2442ad
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1730851Reviewed-by: default avatarKevin Babbitt <kbabbitt@microsoft.com>
Reviewed-by: default avatarAlice Boxhall <aboxhall@chromium.org>
Commit-Queue: Jacques Newman <janewman@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#683439}
parent fbdcbbe7
...@@ -1309,6 +1309,20 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityHeading) { ...@@ -1309,6 +1309,20 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityHeading) {
RunHtmlTest(FILE_PATH_LITERAL("heading.html")); RunHtmlTest(FILE_PATH_LITERAL("heading.html"));
} }
IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityHidden) {
RunAriaTest(FILE_PATH_LITERAL("hidden.html"));
}
IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
AccessibilityHiddenDescribedBy) {
RunAriaTest(FILE_PATH_LITERAL("hidden-described-by.html"));
}
IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
AccessibilityHiddenLabeledBy) {
RunAriaTest(FILE_PATH_LITERAL("hidden-labelled-by.html"));
}
IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityHR) { IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityHR) {
RunHtmlTest(FILE_PATH_LITERAL("hr.html")); RunHtmlTest(FILE_PATH_LITERAL("hr.html"));
} }
......
...@@ -1221,6 +1221,11 @@ void BlinkAXTreeSource::AddImageAnnotations(blink::WebAXObject& src, ...@@ -1221,6 +1221,11 @@ void BlinkAXTreeSource::AddImageAnnotations(blink::WebAXObject& src,
if (!base::FeatureList::IsEnabled(features::kExperimentalAccessibilityLabels)) if (!base::FeatureList::IsEnabled(features::kExperimentalAccessibilityLabels))
return; return;
// Reject ignored objects
if (src.AccessibilityIsIgnored()) {
return;
}
// Reject images that are explicitly empty, or that have a name already. // Reject images that are explicitly empty, or that have a name already.
// //
// In the future, we may annotate some images that have a name // In the future, we may annotate some images that have a name
......
rootWebArea
++genericContainer ignored
++++genericContainer ignored
++++++genericContainer ignored name='span-A1'
++++++++staticText ignored
++++++++genericContainer ignored name='span-A2'
++++++++staticText ignored
++++++++genericContainer description='span-A4' name='span-A3' descriptionFrom=relatedElement describedbyIds=genericContainer
++++++++++genericContainer ignored name='span-A4'
++++++++staticText ignored
++++++genericContainer description='span-A2' name='span-B' descriptionFrom=relatedElement describedbyIds=genericContainer
++++++genericContainer name='span-C'
document
++group Name='span-A3' DescribedBy='span-A4'
++group Name='span-B' DescribedBy='span-A2'
++group Name='span-C'
ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
++IA2_ROLE_SECTION name='span-A3' description='span-A4'
++IA2_ROLE_SECTION name='span-B' description='span-A2'
++IA2_ROLE_SECTION name='span-C'
<!--
@UIA-WIN-ALLOW:Descr*
@BLINK-ALLOW:descr*
@WIN-ALLOW:descr*
-->
<!DOCTYPE html>
<html>
<style>
.hide {
visibility: hidden;
}
.visible {
visibility: visible;
}
</style>
<body>
<div>
<span class="hide" aria-label="span-A1">
<span aria-label="span-A2" id="a2"></span>
<span class="visible" aria-label="span-A3" aria-describedby="a4">
<span class="hide" aria-label="span-A4" id="a4"></span>
</span>
</span>
<span aria-label="span-B" aria-describedby="a2"></span>
<span aria-label="span-C"></span>
</div>
</body>
</html>
rootWebArea
++genericContainer ignored
++++genericContainer ignored
++++++genericContainer ignored name='a1'
++++++++staticText ignored
++++++++genericContainer ignored name='a2'
++++++++staticText ignored
++++++genericContainer name='b'
++++++genericContainer name='c'
ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
++IA2_ROLE_SECTION name='b'
++IA2_ROLE_SECTION name='c'
rootWebArea
++genericContainer ignored
++++genericContainer ignored
++++++genericContainer ignored name='span-A1'
++++++genericContainer name='span-A2'
++++++genericContainer name='span-A4' labelledbyIds=genericContainer
++++++++genericContainer ignored name='span-A4'
++++++genericContainer name='span-A2' labelledbyIds=genericContainer
++++++genericContainer name='span-C'
document
++group Name='span-A2'
++group Name='span-A4' LabeledBy='span-A4'
++group Name='span-A2' LabeledBy='span-A2'
++group Name='span-C'
ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
++IA2_ROLE_SECTION name='span-A2'
++IA2_ROLE_SECTION name='span-A4'
++IA2_ROLE_SECTION name='span-A2'
++IA2_ROLE_SECTION name='span-C'
<!--
@UIA-WIN-ALLOW:Label*
@BLINK-ALLOW:label*
@WIN-ALLOW:label*
-->
<!DOCTYPE html>
<html>
<style>
.hide {
visibility: hidden;
}
.visible {
visibility: visible;
}
</style>
<body>
<div>
<span class="hide" aria-label="span-A1"></span>
<span aria-label="span-A2" id="a2"></span>
<span class="visible" aria-label="span-A3" aria-labelledby="a4">
<span class="hide" aria-label="span-A4" id="a4"></span>
</span>
</span>
<span aria-label="span-B" aria-labelledby="a2"></span>
<span aria-label="span-C"></span>
</div>
</body>
</html>
<!DOCTYPE html>
<html>
<style>
.hide {
visibility: hidden;
}
</style>
<body>
<div>
<span class="hide" aria-label="a1">
<span aria-label="a2"></span>
</span>
<span aria-label="b"></span>
<span aria-label="c"></span>
</div>
</body>
</html>
EVENT_OBJECT_HIDE on <div.a> role=DIV level=2 EVENT_OBJECT_HIDE on <div.a> role=DIV name="Heading" level=2
EVENT_OBJECT_REORDER on <div> role=ROLE_SYSTEM_TOOLBAR IA2_STATE_HORIZONTAL EVENT_OBJECT_REORDER on <div> role=ROLE_SYSTEM_TOOLBAR IA2_STATE_HORIZONTAL
EVENT_OBJECT_SHOW on <div.b> role=ROLE_SYSTEM_GROUPING name="Banner" EVENT_OBJECT_SHOW on <div.b> role=ROLE_SYSTEM_GROUPING name="Banner"
IA2_EVENT_TEXT_INSERTED on <div> role=ROLE_SYSTEM_TOOLBAR IA2_STATE_HORIZONTAL new_text={'<obj>' start=0 end=1} IA2_EVENT_TEXT_INSERTED on <div> role=ROLE_SYSTEM_TOOLBAR IA2_STATE_HORIZONTAL new_text={'<obj>' start=0 end=1}
......
...@@ -1178,14 +1178,28 @@ bool AXObject::ComputeAccessibilityIsIgnoredButIncludedInTree() const { ...@@ -1178,14 +1178,28 @@ bool AXObject::ComputeAccessibilityIsIgnoredButIncludedInTree() const {
if (!GetNode()) if (!GetNode())
return false; return false;
// If the node is part of the user agent shadow dom, or has the explicit
// internal Role::kIgnored, they aren't interesting for paragraph navigation
// or LabelledBy/DescribedBy relationships.
if (RoleValue() == ax::mojom::Role::kIgnored ||
GetNode()->IsInUserAgentShadowRoot()) {
return false;
}
// Always pass through Line Breaking objects, this is necessary to // Always pass through Line Breaking objects, this is necessary to
// detect paragraph edges, which are defined as hard-line breaks. // detect paragraph edges, which are defined as hard-line breaks.
// if (IsLineBreakingObject())
// Though if the node is part of the shadow dom, or has the explicit return true;
// internal Role::kIgnored, they aren't interesting for paragraph
// navigation so exclude those cases. // Allow the browser side ax tree to access "visibility: hidden" nodes. This
return RoleValue() != ax::mojom::Role::kIgnored && // is useful for APIs that return the node referenced by aria-labeledby and
!GetNode()->IsInUserAgentShadowRoot() && IsLineBreakingObject(); // aria-descibedby
if (GetLayoutObject() &&
GetLayoutObject()->Style()->Visibility() == EVisibility::kHidden) {
return true;
}
return false;
} }
const AXObject* AXObject::DatetimeAncestor(int max_levels_to_check) const { const AXObject* AXObject::DatetimeAncestor(int max_levels_to_check) const {
...@@ -1491,8 +1505,15 @@ String AXObject::GetName(ax::mojom::NameFrom& name_from, ...@@ -1491,8 +1505,15 @@ String AXObject::GetName(ax::mojom::NameFrom& name_from,
AXObject::AXObjectVector* name_objects) const { AXObject::AXObjectVector* name_objects) const {
HeapHashSet<Member<const AXObject>> visited; HeapHashSet<Member<const AXObject>> visited;
AXRelatedObjectVector related_objects; AXRelatedObjectVector related_objects;
String text = TextAlternative(false, false, visited, name_from, // For purposes of computing a text alternative, if an ignored node is
&related_objects, nullptr); // included in the tree, assume that it is the target of aria-labelledby or
// aria-describedby, since we can't tell yet whether that's the case. If it
// isn't exposed, the AT will never see the name anyways.
bool hidden_and_ignored_but_included_in_tree =
IsHiddenForTextAlternativeCalculation() &&
AccessibilityIsIgnoredButIncludedInTree();
String text = TextAlternative(false, hidden_and_ignored_but_included_in_tree,
visited, name_from, &related_objects, nullptr);
ax::mojom::Role role = RoleValue(); ax::mojom::Role role = RoleValue();
if (!GetNode() || if (!GetNode() ||
...@@ -1513,8 +1534,16 @@ String AXObject::GetName(NameSources* name_sources) const { ...@@ -1513,8 +1534,16 @@ String AXObject::GetName(NameSources* name_sources) const {
AXObjectSet visited; AXObjectSet visited;
ax::mojom::NameFrom tmp_name_from; ax::mojom::NameFrom tmp_name_from;
AXRelatedObjectVector tmp_related_objects; AXRelatedObjectVector tmp_related_objects;
String text = TextAlternative(false, false, visited, tmp_name_from, // For purposes of computing a text alternative, if an ignored node is
&tmp_related_objects, name_sources); // included in the tree, assume that it is the target of aria-labelledby or
// aria-describedby, since we can't tell yet whether that's the case. If it
// isn't exposed, the AT will never see the name anyways.
bool hidden_and_ignored_but_included_in_tree =
IsHiddenForTextAlternativeCalculation() &&
AccessibilityIsIgnoredButIncludedInTree();
String text =
TextAlternative(false, hidden_and_ignored_but_included_in_tree, visited,
tmp_name_from, &tmp_related_objects, name_sources);
text = text.SimplifyWhiteSpace(IsHTMLSpace<UChar>); text = text.SimplifyWhiteSpace(IsHTMLSpace<UChar>);
return text; return text;
} }
......
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