Commit ced12ea7 authored by Ajit Narayanan's avatar Ajit Narayanan Committed by Commit Bot

Expose user-select:none to accessibility.

This CL makes user-select:none property visible to accessibility
features via a new boolean attribute, kNotUserSelectable. This is,
for example, useful for select-to-speak, which we expect to skip
non-user-selectable text.

The property is default-false in order to be memory-efficient and
backwards-compatible. Most nodes in the DOM will not have user:select
none, so this property will be omitted from the tree unless the user
explicitly marks user:select=none.

Bug: 830106
Change-Id: I7c8ea7b994514c102389a23bfc5d372ea4737da9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2353127
Commit-Queue: Ajit Narayanan <ajitnarayanan@google.com>
Reviewed-by: default avatarNektarios Paisios <nektar@chromium.org>
Reviewed-by: default avatarDominic Mazzoni <dmazzoni@chromium.org>
Reviewed-by: default avatarDavid Tseng <dtseng@chromium.org>
Reviewed-by: default avatarAlex Gough <ajgo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#798398}
parent 296abcac
...@@ -2343,6 +2343,11 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityUl) { ...@@ -2343,6 +2343,11 @@ IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityUl) {
RunHtmlTest(FILE_PATH_LITERAL("ul.html")); RunHtmlTest(FILE_PATH_LITERAL("ul.html"));
} }
IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest,
AccessibilityNotUserSelectable) {
RunCSSTest(FILE_PATH_LITERAL("user-select.html"));
}
IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityVar) { IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityVar) {
RunHtmlTest(FILE_PATH_LITERAL("var.html")); RunHtmlTest(FILE_PATH_LITERAL("var.html"));
} }
......
rootWebArea
++genericContainer ignored
++++genericContainer ignored
++++++genericContainer notUserSelectableStyle=true
++++++++staticText name='1. unselectable' notUserSelectableStyle=true
++++++++++inlineTextBox name='1. unselectable'
++++++genericContainer
++++++++staticText name='2. Selectable'
++++++++++inlineTextBox name='2. Selectable'
++++++genericContainer notUserSelectableStyle=true
++++++++staticText name='3. unselectable' notUserSelectableStyle=true
++++++++++inlineTextBox name='3. unselectable'
++++++genericContainer
++++++++staticText name='4. Selectable'
++++++++++inlineTextBox name='4. Selectable'
++++++list
++++++++listItem
++++++++++listMarker name='• '
++++++++++++staticText name='• '
++++++++++++++inlineTextBox name='• '
++++++++++staticText name='One'
++++++++++++inlineTextBox name='One'
++++++genericContainer ignored notUserSelectableStyle=true
++++++++genericContainer notUserSelectableStyle=true
++++++++++staticText name='Inner' notUserSelectableStyle=true
++++++++++++inlineTextBox name='Inner'
<!--
@BLINK-ALLOW:notUserSelectableStyle*
-->
<div style="user-select:none">1. unselectable </div>
<div style="user-select:text"> 2. Selectable </div>
<div style="user-select:none"> 3. unselectable </div>
<div style="user-select:text"> 4. Selectable </div>
<!-- List bullets are not selectable, but that is not due to user-select,
so they will not have the notUserSelectableStyle attribute -->
<ul>
<li>One</li>
</ul>
<!-- Should inherit -->
<div style="user-select:none">
<div style="user-select:inherit">Inner</div>
</div>
\ No newline at end of file
...@@ -523,6 +523,18 @@ bool AXLayoutObject::IsSelectedFromFocus() const { ...@@ -523,6 +523,18 @@ bool AXLayoutObject::IsSelectedFromFocus() const {
is_selected); is_selected);
} }
// Returns true if the object is marked user-select:none
bool AXLayoutObject::IsNotUserSelectable() const {
if (!GetLayoutObject())
return false;
const ComputedStyle* style = GetLayoutObject()->Style();
if (!style)
return false;
return (style->UserSelect() == EUserSelect::kNone);
}
// Returns true if the node's aria-selected attribute should be set to true // Returns true if the node's aria-selected attribute should be set to true
// when the node is focused. This is true for only a subset of roles. // when the node is focused. This is true for only a subset of roles.
bool AXLayoutObject::SelectionShouldFollowFocus() const { bool AXLayoutObject::SelectionShouldFollowFocus() const {
......
...@@ -96,6 +96,7 @@ class MODULES_EXPORT AXLayoutObject : public AXNodeObject { ...@@ -96,6 +96,7 @@ class MODULES_EXPORT AXLayoutObject : public AXNodeObject {
AccessibilityGrabbedState IsGrabbed() const override; AccessibilityGrabbedState IsGrabbed() const override;
AccessibilitySelectedState IsSelected() const override; AccessibilitySelectedState IsSelected() const override;
bool IsSelectedFromFocus() const override; bool IsSelectedFromFocus() const override;
bool IsNotUserSelectable() const override;
// Whether objects are ignored, i.e. not included in the tree. // Whether objects are ignored, i.e. not included in the tree.
AXObjectInclusion DefaultObjectInclusion( AXObjectInclusion DefaultObjectInclusion(
......
...@@ -790,6 +790,11 @@ void AXObject::Serialize(ui::AXNodeData* node_data) { ...@@ -790,6 +790,11 @@ void AXObject::Serialize(ui::AXNodeData* node_data) {
IsSelectedFromFocus()); IsSelectedFromFocus());
} }
if (IsNotUserSelectable()) {
node_data->AddBoolAttribute(
ax::mojom::blink::BoolAttribute::kNotUserSelectableStyle, true);
}
if (IsRichlyEditable()) if (IsRichlyEditable())
node_data->AddState(ax::mojom::blink::State::kRichlyEditable); node_data->AddState(ax::mojom::blink::State::kRichlyEditable);
......
...@@ -501,6 +501,7 @@ class MODULES_EXPORT AXObject : public GarbageCollected<AXObject> { ...@@ -501,6 +501,7 @@ class MODULES_EXPORT AXObject : public GarbageCollected<AXObject> {
// Is the object selected because selection is following focus? // Is the object selected because selection is following focus?
virtual bool IsSelectedFromFocus() const { return false; } virtual bool IsSelectedFromFocus() const { return false; }
virtual bool IsSelectedOptionActive() const { return false; } virtual bool IsSelectedOptionActive() const { return false; }
virtual bool IsNotUserSelectable() const { return false; }
virtual bool IsVisible() const; virtual bool IsVisible() const;
virtual bool IsVisited() const { return false; } virtual bool IsVisited() const { return false; }
......
...@@ -1882,6 +1882,8 @@ const char* ToString(ax::mojom::BoolAttribute bool_attribute) { ...@@ -1882,6 +1882,8 @@ const char* ToString(ax::mojom::BoolAttribute bool_attribute) {
return "clickable"; return "clickable";
case ax::mojom::BoolAttribute::kClipsChildren: case ax::mojom::BoolAttribute::kClipsChildren:
return "clipsChildren"; return "clipsChildren";
case ax::mojom::BoolAttribute::kNotUserSelectableStyle:
return "notUserSelectableStyle";
case ax::mojom::BoolAttribute::kSelected: case ax::mojom::BoolAttribute::kSelected:
return "selected"; return "selected";
case ax::mojom::BoolAttribute::kSelectedFromFocus: case ax::mojom::BoolAttribute::kSelectedFromFocus:
...@@ -1926,6 +1928,8 @@ ax::mojom::BoolAttribute ParseBoolAttribute(const char* bool_attribute) { ...@@ -1926,6 +1928,8 @@ ax::mojom::BoolAttribute ParseBoolAttribute(const char* bool_attribute) {
return ax::mojom::BoolAttribute::kClickable; return ax::mojom::BoolAttribute::kClickable;
if (0 == strcmp(bool_attribute, "clipsChildren")) if (0 == strcmp(bool_attribute, "clipsChildren"))
return ax::mojom::BoolAttribute::kClipsChildren; return ax::mojom::BoolAttribute::kClipsChildren;
if (0 == strcmp(bool_attribute, "notUserSelectableStyle"))
return ax::mojom::BoolAttribute::kNotUserSelectableStyle;
if (0 == strcmp(bool_attribute, "selected")) if (0 == strcmp(bool_attribute, "selected"))
return ax::mojom::BoolAttribute::kSelected; return ax::mojom::BoolAttribute::kSelected;
if (0 == strcmp(bool_attribute, "selectedFromFocus")) if (0 == strcmp(bool_attribute, "selectedFromFocus"))
......
...@@ -733,6 +733,12 @@ enum BoolAttribute { ...@@ -733,6 +733,12 @@ enum BoolAttribute {
// overflow: hidden or clip children by default. // overflow: hidden or clip children by default.
kClipsChildren, kClipsChildren,
// Indicates that this node is not selectable because the style has
// user-select: none. Note that there may be other reasons why a node is
// not selectable - for example, bullets in a list. However, this attribute
// is only set on user-select: none.
kNotUserSelectableStyle,
// Indicates whether this node is selected or unselected. // Indicates whether this node is selected or unselected.
kSelected, kSelected,
......
...@@ -1575,6 +1575,9 @@ std::string AXNodeData::ToString() const { ...@@ -1575,6 +1575,9 @@ std::string AXNodeData::ToString() const {
case ax::mojom::BoolAttribute::kClipsChildren: case ax::mojom::BoolAttribute::kClipsChildren:
result += " clips_children=" + value; result += " clips_children=" + value;
break; break;
case ax::mojom::BoolAttribute::kNotUserSelectableStyle:
result += " not_user_selectable=" + value;
break;
case ax::mojom::BoolAttribute::kSelected: case ax::mojom::BoolAttribute::kSelected:
result += " selected=" + value; result += " selected=" + value;
break; break;
......
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