1 | /* |
2 | * Copyright (C) 2008 Apple Ltd. |
3 | * Copyright (C) 2008 Alp Toker <alp@atoker.com> |
4 | * |
5 | * This library is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Library General Public |
7 | * License as published by the Free Software Foundation; either |
8 | * version 2 of the License, or (at your option) any later version. |
9 | * |
10 | * This library is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Library General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Library General Public License |
16 | * along with this library; see the file COPYING.LIB. If not, write to |
17 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
18 | * Boston, MA 02110-1301, USA. |
19 | */ |
20 | |
21 | #include "config.h" |
22 | #include "AccessibilityObject.h" |
23 | |
24 | #include "HTMLSpanElement.h" |
25 | #include "RenderBlock.h" |
26 | #include "RenderInline.h" |
27 | #include "RenderIterator.h" |
28 | #include "RenderTableCell.h" |
29 | #include "RenderText.h" |
30 | #include "TextControlInnerElements.h" |
31 | #include <glib-object.h> |
32 | |
33 | #if HAVE(ACCESSIBILITY) |
34 | |
35 | namespace WebCore { |
36 | |
37 | bool AccessibilityObject::accessibilityIgnoreAttachment() const |
38 | { |
39 | return false; |
40 | } |
41 | |
42 | AccessibilityObjectInclusion AccessibilityObject::accessibilityPlatformIncludesObject() const |
43 | { |
44 | AccessibilityObject* parent = parentObject(); |
45 | if (!parent) |
46 | return AccessibilityObjectInclusion::DefaultBehavior; |
47 | |
48 | // If the author has provided a role, platform-specific inclusion likely doesn't apply. |
49 | if (ariaRoleAttribute() != AccessibilityRole::Unknown) |
50 | return AccessibilityObjectInclusion::DefaultBehavior; |
51 | |
52 | AccessibilityRole role = roleValue(); |
53 | // We expose the slider as a whole but not its value indicator. |
54 | if (role == AccessibilityRole::SliderThumb) |
55 | return AccessibilityObjectInclusion::IgnoreObject; |
56 | |
57 | // When a list item is made up entirely of children (e.g. paragraphs) |
58 | // the list item gets ignored. We need it. |
59 | if (isGroup() && parent->isList()) |
60 | return AccessibilityObjectInclusion::IncludeObject; |
61 | |
62 | // Entries and password fields have extraneous children which we want to ignore. |
63 | if (parent->isPasswordField() || parent->isTextControl()) |
64 | return AccessibilityObjectInclusion::IgnoreObject; |
65 | |
66 | // Include all tables, even layout tables. The AT can decide what to do with each. |
67 | if (role == AccessibilityRole::Cell || role == AccessibilityRole::Table || role == AccessibilityRole::ColumnHeader || role == AccessibilityRole::RowHeader) |
68 | return AccessibilityObjectInclusion::IncludeObject; |
69 | |
70 | // The object containing the text should implement AtkText itself. |
71 | // However, WebCore also maps ARIA's "text" role to the StaticTextRole. |
72 | if (role == AccessibilityRole::StaticText) |
73 | return ariaRoleAttribute() != AccessibilityRole::Unknown ? AccessibilityObjectInclusion::DefaultBehavior : AccessibilityObjectInclusion::IgnoreObject; |
74 | |
75 | // Include all list items, regardless they have or not inline children |
76 | if (role == AccessibilityRole::ListItem) |
77 | return AccessibilityObjectInclusion::IncludeObject; |
78 | |
79 | // Bullets/numbers for list items shouldn't be exposed as AtkObjects. |
80 | if (role == AccessibilityRole::ListMarker) |
81 | return AccessibilityObjectInclusion::IgnoreObject; |
82 | |
83 | // Never expose an unknown object, since AT's won't know what to |
84 | // do with them. This is what is done on the Mac as well. |
85 | if (role == AccessibilityRole::Unknown) |
86 | return AccessibilityObjectInclusion::IgnoreObject; |
87 | |
88 | if (role == AccessibilityRole::Inline) |
89 | return AccessibilityObjectInclusion::IncludeObject; |
90 | |
91 | // Lines past this point only make sense for AccessibilityRenderObjects. |
92 | RenderObject* renderObject = renderer(); |
93 | if (!renderObject) |
94 | return AccessibilityObjectInclusion::DefaultBehavior; |
95 | |
96 | // We always want to include paragraphs that have rendered content. |
97 | // WebCore Accessibility does so unless there is a RenderBlock child. |
98 | if (role == AccessibilityRole::Paragraph) { |
99 | auto child = childrenOfType<RenderBlock>(downcast<RenderElement>(*renderObject)).first(); |
100 | return child ? AccessibilityObjectInclusion::IncludeObject : AccessibilityObjectInclusion::DefaultBehavior; |
101 | } |
102 | |
103 | // We always want to include table cells (layout and CSS) that have rendered text content. |
104 | if (is<RenderTableCell>(renderObject)) { |
105 | for (const auto& child : childrenOfType<RenderObject>(downcast<RenderElement>(*renderObject))) { |
106 | if (is<RenderInline>(child) || is<RenderText>(child) || is<HTMLSpanElement>(child.node())) |
107 | return AccessibilityObjectInclusion::IncludeObject; |
108 | } |
109 | return AccessibilityObjectInclusion::DefaultBehavior; |
110 | } |
111 | |
112 | if (renderObject->isAnonymousBlock()) { |
113 | // The text displayed by an ARIA menu item is exposed through the accessible name. |
114 | if (parent->isMenuItem()) |
115 | return AccessibilityObjectInclusion::IgnoreObject; |
116 | |
117 | // The text displayed in headings is typically exposed in the heading itself. |
118 | if (parent->isHeading()) |
119 | return AccessibilityObjectInclusion::IgnoreObject; |
120 | |
121 | // The text displayed in list items is typically exposed in the list item itself. |
122 | if (parent->isListItem()) |
123 | return AccessibilityObjectInclusion::IgnoreObject; |
124 | |
125 | // The text displayed in links is typically exposed in the link itself. |
126 | if (parent->isLink()) |
127 | return AccessibilityObjectInclusion::IgnoreObject; |
128 | |
129 | // FIXME: This next one needs some further consideration. But paragraphs are not |
130 | // typically huge (like divs). And ignoring anonymous block children of paragraphs |
131 | // will preserve existing behavior. |
132 | if (parent->roleValue() == AccessibilityRole::Paragraph) |
133 | return AccessibilityObjectInclusion::IgnoreObject; |
134 | |
135 | return AccessibilityObjectInclusion::DefaultBehavior; |
136 | } |
137 | |
138 | Node* node = renderObject->node(); |
139 | if (!node) |
140 | return AccessibilityObjectInclusion::DefaultBehavior; |
141 | |
142 | // We don't want <span> elements to show up in the accessibility hierarchy unless |
143 | // we have good reasons for that (e.g. focusable or visible because of containing |
144 | // a meaningful accessible name, maybe set through ARIA), so we can use |
145 | // atk_component_grab_focus() to set the focus to it. |
146 | if (is<HTMLSpanElement>(node) && !canSetFocusAttribute() && !hasAttributesRequiredForInclusion() && !supportsARIAAttributes()) |
147 | return AccessibilityObjectInclusion::IgnoreObject; |
148 | |
149 | // If we include TextControlInnerTextElement children, changes to those children |
150 | // will result in focus and text notifications that suggest the user is no longer |
151 | // in the control. This can be especially problematic for screen reader users with |
152 | // key echo enabled when typing in a password input. |
153 | if (is<TextControlInnerTextElement>(node)) |
154 | return AccessibilityObjectInclusion::IgnoreObject; |
155 | |
156 | return AccessibilityObjectInclusion::DefaultBehavior; |
157 | } |
158 | |
159 | bool AccessibilityObject::() const |
160 | { |
161 | // Check type for the AccessibilityObject. |
162 | if (isTextControl() || isWebArea() || isGroup() || isLink() || isHeading() || isListItem() || isTableCell()) |
163 | return true; |
164 | |
165 | // Check roles as the last fallback mechanism. |
166 | AccessibilityRole role = roleValue(); |
167 | return role == AccessibilityRole::Paragraph || role == AccessibilityRole::Label || role == AccessibilityRole::Div || role == AccessibilityRole::Form || role == AccessibilityRole::Pre; |
168 | } |
169 | |
170 | unsigned AccessibilityObject::() const |
171 | { |
172 | unsigned textLength = text().length(); |
173 | |
174 | if (textLength) |
175 | return textLength; |
176 | |
177 | // Gtk ATs need this for all text objects; not just text controls. |
178 | Node* node = this->node(); |
179 | RenderObject* renderer = node ? node->renderer() : nullptr; |
180 | if (is<RenderText>(renderer)) |
181 | textLength = downcast<RenderText>(*renderer).text().length(); |
182 | |
183 | // Get the text length from the elements under the |
184 | // accessibility object if the value is still zero. |
185 | if (!textLength && allowsTextRanges()) |
186 | textLength = textUnderElement(AccessibilityTextUnderElementMode(AccessibilityTextUnderElementMode::TextUnderElementModeIncludeAllChildren)).length(); |
187 | |
188 | return textLength; |
189 | } |
190 | |
191 | } // namespace WebCore |
192 | |
193 | #endif // HAVE(ACCESSIBILITY) |
194 | |