OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "content/renderer/accessibility_node_serializer.h" | |
6 | |
7 #include <set> | |
8 | |
9 #include "base/string_number_conversions.h" | |
10 #include "base/string_util.h" | |
11 #include "base/utf_string_conversions.h" | |
12 #include "third_party/WebKit/Source/Platform/chromium/public/WebRect.h" | |
13 #include "third_party/WebKit/Source/Platform/chromium/public/WebSize.h" | |
14 #include "third_party/WebKit/Source/Platform/chromium/public/WebString.h" | |
15 #include "third_party/WebKit/Source/Platform/chromium/public/WebVector.h" | |
16 #include "third_party/WebKit/Source/WebKit/chromium/public/WebAccessibilityObjec
t.h" | |
17 #include "third_party/WebKit/Source/WebKit/chromium/public/WebAccessibilityRole.
h" | |
18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" | |
19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocumentType.h" | |
20 #include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h" | |
21 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFormControlElement
.h" | |
22 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" | |
23 #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputElement.h" | |
24 #include "third_party/WebKit/Source/WebKit/chromium/public/WebNode.h" | |
25 | |
26 using WebKit::WebAccessibilityRole; | |
27 using WebKit::WebAccessibilityObject; | |
28 using WebKit::WebDocument; | |
29 using WebKit::WebDocumentType; | |
30 using WebKit::WebElement; | |
31 using WebKit::WebNode; | |
32 using WebKit::WebVector; | |
33 | |
34 namespace content { | |
35 namespace { | |
36 | |
37 // Returns true if |ancestor| is the first unignored parent of |child|, | |
38 // which means that when walking up the parent chain from |child|, | |
39 // |ancestor| is the *first* ancestor that isn't marked as | |
40 // accessibilityIsIgnored(). | |
41 bool IsParentUnignoredOf(const WebAccessibilityObject& ancestor, | |
42 const WebAccessibilityObject& child) { | |
43 WebAccessibilityObject parent = child.parentObject(); | |
44 while (!parent.isDetached() && parent.accessibilityIsIgnored()) | |
45 parent = parent.parentObject(); | |
46 return parent.equals(ancestor); | |
47 } | |
48 | |
49 // Provides a conversion between the WebKit::WebAccessibilityRole and a role | |
50 // supported on the Browser side. Listed alphabetically by the | |
51 // WebKit::WebAccessibilityRole (except for default role). | |
52 AccessibilityNodeData::Role ConvertRole(WebKit::WebAccessibilityRole role) { | |
53 switch (role) { | |
54 case WebKit::WebAccessibilityRoleAnnotation: | |
55 return AccessibilityNodeData::ROLE_ANNOTATION; | |
56 case WebKit::WebAccessibilityRoleApplication: | |
57 return AccessibilityNodeData::ROLE_APPLICATION; | |
58 case WebKit::WebAccessibilityRoleApplicationAlert: | |
59 return AccessibilityNodeData::ROLE_ALERT; | |
60 case WebKit::WebAccessibilityRoleApplicationAlertDialog: | |
61 return AccessibilityNodeData::ROLE_ALERT_DIALOG; | |
62 case WebKit::WebAccessibilityRoleApplicationDialog: | |
63 return AccessibilityNodeData::ROLE_DIALOG; | |
64 case WebKit::WebAccessibilityRoleApplicationLog: | |
65 return AccessibilityNodeData::ROLE_LOG; | |
66 case WebKit::WebAccessibilityRoleApplicationMarquee: | |
67 return AccessibilityNodeData::ROLE_MARQUEE; | |
68 case WebKit::WebAccessibilityRoleApplicationStatus: | |
69 return AccessibilityNodeData::ROLE_STATUS; | |
70 case WebKit::WebAccessibilityRoleApplicationTimer: | |
71 return AccessibilityNodeData::ROLE_TIMER; | |
72 case WebKit::WebAccessibilityRoleBrowser: | |
73 return AccessibilityNodeData::ROLE_BROWSER; | |
74 case WebKit::WebAccessibilityRoleBusyIndicator: | |
75 return AccessibilityNodeData::ROLE_BUSY_INDICATOR; | |
76 case WebKit::WebAccessibilityRoleButton: | |
77 return AccessibilityNodeData::ROLE_BUTTON; | |
78 case WebKit::WebAccessibilityRoleCanvas: | |
79 return AccessibilityNodeData::ROLE_CANVAS; | |
80 case WebKit::WebAccessibilityRoleCell: | |
81 return AccessibilityNodeData::ROLE_CELL; | |
82 case WebKit::WebAccessibilityRoleCheckBox: | |
83 return AccessibilityNodeData::ROLE_CHECKBOX; | |
84 case WebKit::WebAccessibilityRoleColorWell: | |
85 return AccessibilityNodeData::ROLE_COLOR_WELL; | |
86 case WebKit::WebAccessibilityRoleColumn: | |
87 return AccessibilityNodeData::ROLE_COLUMN; | |
88 case WebKit::WebAccessibilityRoleColumnHeader: | |
89 return AccessibilityNodeData::ROLE_COLUMN_HEADER; | |
90 case WebKit::WebAccessibilityRoleComboBox: | |
91 return AccessibilityNodeData::ROLE_COMBO_BOX; | |
92 case WebKit::WebAccessibilityRoleDirectory: | |
93 return AccessibilityNodeData::ROLE_DIRECTORY; | |
94 case WebKit::WebAccessibilityRoleDisclosureTriangle: | |
95 return AccessibilityNodeData::ROLE_DISCLOSURE_TRIANGLE; | |
96 case WebKit::WebAccessibilityRoleDiv: | |
97 return AccessibilityNodeData::ROLE_DIV; | |
98 case WebKit::WebAccessibilityRoleDocument: | |
99 return AccessibilityNodeData::ROLE_DOCUMENT; | |
100 case WebKit::WebAccessibilityRoleDocumentArticle: | |
101 return AccessibilityNodeData::ROLE_ARTICLE; | |
102 case WebKit::WebAccessibilityRoleDocumentMath: | |
103 return AccessibilityNodeData::ROLE_MATH; | |
104 case WebKit::WebAccessibilityRoleDocumentNote: | |
105 return AccessibilityNodeData::ROLE_NOTE; | |
106 case WebKit::WebAccessibilityRoleDocumentRegion: | |
107 return AccessibilityNodeData::ROLE_REGION; | |
108 case WebKit::WebAccessibilityRoleDrawer: | |
109 return AccessibilityNodeData::ROLE_DRAWER; | |
110 case WebKit::WebAccessibilityRoleEditableText: | |
111 return AccessibilityNodeData::ROLE_EDITABLE_TEXT; | |
112 case WebKit::WebAccessibilityRoleFooter: | |
113 return AccessibilityNodeData::ROLE_FOOTER; | |
114 case WebKit::WebAccessibilityRoleForm: | |
115 return AccessibilityNodeData::ROLE_FORM; | |
116 case WebKit::WebAccessibilityRoleGrid: | |
117 return AccessibilityNodeData::ROLE_GRID; | |
118 case WebKit::WebAccessibilityRoleGroup: | |
119 return AccessibilityNodeData::ROLE_GROUP; | |
120 case WebKit::WebAccessibilityRoleGrowArea: | |
121 return AccessibilityNodeData::ROLE_GROW_AREA; | |
122 case WebKit::WebAccessibilityRoleHeading: | |
123 return AccessibilityNodeData::ROLE_HEADING; | |
124 case WebKit::WebAccessibilityRoleHelpTag: | |
125 return AccessibilityNodeData::ROLE_HELP_TAG; | |
126 case WebKit::WebAccessibilityRoleHorizontalRule: | |
127 return AccessibilityNodeData::ROLE_HORIZONTAL_RULE; | |
128 case WebKit::WebAccessibilityRoleIgnored: | |
129 return AccessibilityNodeData::ROLE_IGNORED; | |
130 case WebKit::WebAccessibilityRoleImage: | |
131 return AccessibilityNodeData::ROLE_IMAGE; | |
132 case WebKit::WebAccessibilityRoleImageMap: | |
133 return AccessibilityNodeData::ROLE_IMAGE_MAP; | |
134 case WebKit::WebAccessibilityRoleImageMapLink: | |
135 return AccessibilityNodeData::ROLE_IMAGE_MAP_LINK; | |
136 case WebKit::WebAccessibilityRoleIncrementor: | |
137 return AccessibilityNodeData::ROLE_INCREMENTOR; | |
138 case WebKit::WebAccessibilityRoleLabel: | |
139 return AccessibilityNodeData::ROLE_LABEL; | |
140 case WebKit::WebAccessibilityRoleLandmarkApplication: | |
141 return AccessibilityNodeData::ROLE_LANDMARK_APPLICATION; | |
142 case WebKit::WebAccessibilityRoleLandmarkBanner: | |
143 return AccessibilityNodeData::ROLE_LANDMARK_BANNER; | |
144 case WebKit::WebAccessibilityRoleLandmarkComplementary: | |
145 return AccessibilityNodeData::ROLE_LANDMARK_COMPLEMENTARY; | |
146 case WebKit::WebAccessibilityRoleLandmarkContentInfo: | |
147 return AccessibilityNodeData::ROLE_LANDMARK_CONTENTINFO; | |
148 case WebKit::WebAccessibilityRoleLandmarkMain: | |
149 return AccessibilityNodeData::ROLE_LANDMARK_MAIN; | |
150 case WebKit::WebAccessibilityRoleLandmarkNavigation: | |
151 return AccessibilityNodeData::ROLE_LANDMARK_NAVIGATION; | |
152 case WebKit::WebAccessibilityRoleLandmarkSearch: | |
153 return AccessibilityNodeData::ROLE_LANDMARK_SEARCH; | |
154 case WebKit::WebAccessibilityRoleLink: | |
155 return AccessibilityNodeData::ROLE_LINK; | |
156 case WebKit::WebAccessibilityRoleList: | |
157 return AccessibilityNodeData::ROLE_LIST; | |
158 case WebKit::WebAccessibilityRoleListBox: | |
159 return AccessibilityNodeData::ROLE_LISTBOX; | |
160 case WebKit::WebAccessibilityRoleListBoxOption: | |
161 return AccessibilityNodeData::ROLE_LISTBOX_OPTION; | |
162 case WebKit::WebAccessibilityRoleListItem: | |
163 return AccessibilityNodeData::ROLE_LIST_ITEM; | |
164 case WebKit::WebAccessibilityRoleListMarker: | |
165 return AccessibilityNodeData::ROLE_LIST_MARKER; | |
166 case WebKit::WebAccessibilityRoleMatte: | |
167 return AccessibilityNodeData::ROLE_MATTE; | |
168 case WebKit::WebAccessibilityRoleMenu: | |
169 return AccessibilityNodeData::ROLE_MENU; | |
170 case WebKit::WebAccessibilityRoleMenuBar: | |
171 return AccessibilityNodeData::ROLE_MENU_BAR; | |
172 case WebKit::WebAccessibilityRoleMenuButton: | |
173 return AccessibilityNodeData::ROLE_MENU_BUTTON; | |
174 case WebKit::WebAccessibilityRoleMenuItem: | |
175 return AccessibilityNodeData::ROLE_MENU_ITEM; | |
176 case WebKit::WebAccessibilityRoleMenuListOption: | |
177 return AccessibilityNodeData::ROLE_MENU_LIST_OPTION; | |
178 case WebKit::WebAccessibilityRoleMenuListPopup: | |
179 return AccessibilityNodeData::ROLE_MENU_LIST_POPUP; | |
180 case WebKit::WebAccessibilityRoleOutline: | |
181 return AccessibilityNodeData::ROLE_OUTLINE; | |
182 case WebKit::WebAccessibilityRoleParagraph: | |
183 return AccessibilityNodeData::ROLE_PARAGRAPH; | |
184 case WebKit::WebAccessibilityRolePopUpButton: | |
185 return AccessibilityNodeData::ROLE_POPUP_BUTTON; | |
186 case WebKit::WebAccessibilityRolePresentational: | |
187 return AccessibilityNodeData::ROLE_PRESENTATIONAL; | |
188 case WebKit::WebAccessibilityRoleProgressIndicator: | |
189 return AccessibilityNodeData::ROLE_PROGRESS_INDICATOR; | |
190 case WebKit::WebAccessibilityRoleRadioButton: | |
191 return AccessibilityNodeData::ROLE_RADIO_BUTTON; | |
192 case WebKit::WebAccessibilityRoleRadioGroup: | |
193 return AccessibilityNodeData::ROLE_RADIO_GROUP; | |
194 case WebKit::WebAccessibilityRoleRow: | |
195 return AccessibilityNodeData::ROLE_ROW; | |
196 case WebKit::WebAccessibilityRoleRowHeader: | |
197 return AccessibilityNodeData::ROLE_ROW_HEADER; | |
198 case WebKit::WebAccessibilityRoleRuler: | |
199 return AccessibilityNodeData::ROLE_RULER; | |
200 case WebKit::WebAccessibilityRoleRulerMarker: | |
201 return AccessibilityNodeData::ROLE_RULER_MARKER; | |
202 case WebKit::WebAccessibilityRoleScrollArea: | |
203 return AccessibilityNodeData::ROLE_SCROLLAREA; | |
204 case WebKit::WebAccessibilityRoleScrollBar: | |
205 return AccessibilityNodeData::ROLE_SCROLLBAR; | |
206 case WebKit::WebAccessibilityRoleSheet: | |
207 return AccessibilityNodeData::ROLE_SHEET; | |
208 case WebKit::WebAccessibilityRoleSlider: | |
209 return AccessibilityNodeData::ROLE_SLIDER; | |
210 case WebKit::WebAccessibilityRoleSliderThumb: | |
211 return AccessibilityNodeData::ROLE_SLIDER_THUMB; | |
212 case WebKit::WebAccessibilityRoleSpinButton: | |
213 return AccessibilityNodeData::ROLE_SPIN_BUTTON; | |
214 case WebKit::WebAccessibilityRoleSpinButtonPart: | |
215 return AccessibilityNodeData::ROLE_SPIN_BUTTON_PART; | |
216 case WebKit::WebAccessibilityRoleSplitGroup: | |
217 return AccessibilityNodeData::ROLE_SPLIT_GROUP; | |
218 case WebKit::WebAccessibilityRoleSplitter: | |
219 return AccessibilityNodeData::ROLE_SPLITTER; | |
220 case WebKit::WebAccessibilityRoleStaticText: | |
221 return AccessibilityNodeData::ROLE_STATIC_TEXT; | |
222 case WebKit::WebAccessibilityRoleSVGRoot: | |
223 return AccessibilityNodeData::ROLE_SVG_ROOT; | |
224 case WebKit::WebAccessibilityRoleSystemWide: | |
225 return AccessibilityNodeData::ROLE_SYSTEM_WIDE; | |
226 case WebKit::WebAccessibilityRoleTab: | |
227 return AccessibilityNodeData::ROLE_TAB; | |
228 case WebKit::WebAccessibilityRoleTabGroup: | |
229 return AccessibilityNodeData::ROLE_TAB_GROUP_UNUSED; | |
230 case WebKit::WebAccessibilityRoleTabList: | |
231 return AccessibilityNodeData::ROLE_TAB_LIST; | |
232 case WebKit::WebAccessibilityRoleTabPanel: | |
233 return AccessibilityNodeData::ROLE_TAB_PANEL; | |
234 case WebKit::WebAccessibilityRoleTable: | |
235 return AccessibilityNodeData::ROLE_TABLE; | |
236 case WebKit::WebAccessibilityRoleTableHeaderContainer: | |
237 return AccessibilityNodeData::ROLE_TABLE_HEADER_CONTAINER; | |
238 case WebKit::WebAccessibilityRoleTextArea: | |
239 return AccessibilityNodeData::ROLE_TEXTAREA; | |
240 case WebKit::WebAccessibilityRoleTextField: | |
241 return AccessibilityNodeData::ROLE_TEXT_FIELD; | |
242 case WebKit::WebAccessibilityRoleToggleButton: | |
243 return AccessibilityNodeData::ROLE_TOGGLE_BUTTON; | |
244 case WebKit::WebAccessibilityRoleToolbar: | |
245 return AccessibilityNodeData::ROLE_TOOLBAR; | |
246 case WebKit::WebAccessibilityRoleTreeGrid: | |
247 return AccessibilityNodeData::ROLE_TREE_GRID; | |
248 case WebKit::WebAccessibilityRoleTreeItemRole: | |
249 return AccessibilityNodeData::ROLE_TREE_ITEM; | |
250 case WebKit::WebAccessibilityRoleTreeRole: | |
251 return AccessibilityNodeData::ROLE_TREE; | |
252 case WebKit::WebAccessibilityRoleUserInterfaceTooltip: | |
253 return AccessibilityNodeData::ROLE_TOOLTIP; | |
254 case WebKit::WebAccessibilityRoleValueIndicator: | |
255 return AccessibilityNodeData::ROLE_VALUE_INDICATOR; | |
256 case WebKit::WebAccessibilityRoleWebArea: | |
257 return AccessibilityNodeData::ROLE_WEB_AREA; | |
258 case WebKit::WebAccessibilityRoleWebCoreLink: | |
259 return AccessibilityNodeData::ROLE_WEBCORE_LINK; | |
260 case WebKit::WebAccessibilityRoleWindow: | |
261 return AccessibilityNodeData::ROLE_WINDOW; | |
262 | |
263 default: | |
264 return AccessibilityNodeData::ROLE_UNKNOWN; | |
265 } | |
266 } | |
267 | |
268 // Provides a conversion between the WebAccessibilityObject state | |
269 // accessors and a state bitmask that can be serialized and sent to the | |
270 // Browser process. Rare state are sent as boolean attributes instead. | |
271 uint32 ConvertState(const WebAccessibilityObject& o) { | |
272 uint32 state = 0; | |
273 if (o.isChecked()) | |
274 state |= (1 << AccessibilityNodeData::STATE_CHECKED); | |
275 | |
276 if (o.isCollapsed()) | |
277 state |= (1 << AccessibilityNodeData::STATE_COLLAPSED); | |
278 | |
279 if (o.canSetFocusAttribute()) | |
280 state |= (1 << AccessibilityNodeData::STATE_FOCUSABLE); | |
281 | |
282 if (o.isFocused()) | |
283 state |= (1 << AccessibilityNodeData::STATE_FOCUSED); | |
284 | |
285 if (o.roleValue() == WebKit::WebAccessibilityRolePopUpButton || | |
286 o.ariaHasPopup()) { | |
287 state |= (1 << AccessibilityNodeData::STATE_HASPOPUP); | |
288 if (!o.isCollapsed()) | |
289 state |= (1 << AccessibilityNodeData::STATE_EXPANDED); | |
290 } | |
291 | |
292 if (o.isHovered()) | |
293 state |= (1 << AccessibilityNodeData::STATE_HOTTRACKED); | |
294 | |
295 if (o.isIndeterminate()) | |
296 state |= (1 << AccessibilityNodeData::STATE_INDETERMINATE); | |
297 | |
298 if (!o.isVisible()) | |
299 state |= (1 << AccessibilityNodeData::STATE_INVISIBLE); | |
300 | |
301 if (o.isLinked()) | |
302 state |= (1 << AccessibilityNodeData::STATE_LINKED); | |
303 | |
304 if (o.isMultiSelectable()) | |
305 state |= (1 << AccessibilityNodeData::STATE_MULTISELECTABLE); | |
306 | |
307 if (o.isOffScreen()) | |
308 state |= (1 << AccessibilityNodeData::STATE_OFFSCREEN); | |
309 | |
310 if (o.isPressed()) | |
311 state |= (1 << AccessibilityNodeData::STATE_PRESSED); | |
312 | |
313 if (o.isPasswordField()) | |
314 state |= (1 << AccessibilityNodeData::STATE_PROTECTED); | |
315 | |
316 if (o.isReadOnly()) | |
317 state |= (1 << AccessibilityNodeData::STATE_READONLY); | |
318 | |
319 if (o.isRequired()) | |
320 state |= (1 << AccessibilityNodeData::STATE_REQUIRED); | |
321 | |
322 if (o.canSetSelectedAttribute()) | |
323 state |= (1 << AccessibilityNodeData::STATE_SELECTABLE); | |
324 | |
325 if (o.isSelected()) | |
326 state |= (1 << AccessibilityNodeData::STATE_SELECTED); | |
327 | |
328 if (o.isVisited()) | |
329 state |= (1 << AccessibilityNodeData::STATE_TRAVERSED); | |
330 | |
331 if (!o.isEnabled()) | |
332 state |= (1 << AccessibilityNodeData::STATE_UNAVAILABLE); | |
333 | |
334 if (o.isVertical()) | |
335 state |= (1 << AccessibilityNodeData::STATE_VERTICAL); | |
336 | |
337 if (o.isVisited()) | |
338 state |= (1 << AccessibilityNodeData::STATE_VISITED); | |
339 | |
340 return state; | |
341 } | |
342 | |
343 } // Anonymous namespace | |
344 | |
345 void SerializeAccessibilityNode( | |
346 const WebAccessibilityObject& src, | |
347 AccessibilityNodeData* dst, | |
348 bool include_children) { | |
349 dst->name = src.title(); | |
350 dst->role = ConvertRole(src.roleValue()); | |
351 dst->state = ConvertState(src); | |
352 dst->location = src.boundingBoxRect(); | |
353 dst->id = src.axID(); | |
354 | |
355 if (src.valueDescription().length()) | |
356 dst->value = src.valueDescription(); | |
357 else | |
358 dst->value = src.stringValue(); | |
359 | |
360 if (dst->role == AccessibilityNodeData::ROLE_COLOR_WELL) { | |
361 int r, g, b; | |
362 src.colorValue(r, g, b); | |
363 dst->int_attributes[dst->ATTR_COLOR_VALUE_RED] = r; | |
364 dst->int_attributes[dst->ATTR_COLOR_VALUE_GREEN] = g; | |
365 dst->int_attributes[dst->ATTR_COLOR_VALUE_BLUE] = b; | |
366 } | |
367 | |
368 if (src.accessKey().length()) | |
369 dst->string_attributes[dst->ATTR_ACCESS_KEY] = src.accessKey(); | |
370 if (src.actionVerb().length()) | |
371 dst->string_attributes[dst->ATTR_ACTION] = src.actionVerb(); | |
372 if (src.isAriaReadOnly()) | |
373 dst->bool_attributes[dst->ATTR_ARIA_READONLY] = true; | |
374 if (src.isButtonStateMixed()) | |
375 dst->bool_attributes[dst->ATTR_BUTTON_MIXED] = true; | |
376 if (src.canSetValueAttribute()) | |
377 dst->bool_attributes[dst->ATTR_CAN_SET_VALUE] = true; | |
378 if (src.accessibilityDescription().length()) | |
379 dst->string_attributes[dst->ATTR_DESCRIPTION] = | |
380 src.accessibilityDescription(); | |
381 if (src.hasComputedStyle()) | |
382 dst->string_attributes[dst->ATTR_DISPLAY] = src.computedStyleDisplay(); | |
383 if (src.helpText().length()) | |
384 dst->string_attributes[dst->ATTR_HELP] = src.helpText(); | |
385 if (src.keyboardShortcut().length()) | |
386 dst->string_attributes[dst->ATTR_SHORTCUT] = src.keyboardShortcut(); | |
387 if (!src.titleUIElement().isDetached()) { | |
388 dst->int_attributes[dst->ATTR_TITLE_UI_ELEMENT] = | |
389 src.titleUIElement().axID(); | |
390 } | |
391 if (!src.url().isEmpty()) | |
392 dst->string_attributes[dst->ATTR_URL] = src.url().spec().utf16(); | |
393 | |
394 if (dst->role == dst->ROLE_HEADING) | |
395 dst->int_attributes[dst->ATTR_HIERARCHICAL_LEVEL] = src.headingLevel(); | |
396 else if ((dst->role == dst->ROLE_TREE_ITEM || dst->role == dst->ROLE_ROW) && | |
397 src.hierarchicalLevel() > 0) { | |
398 dst->int_attributes[dst->ATTR_HIERARCHICAL_LEVEL] = src.hierarchicalLevel(); | |
399 } | |
400 | |
401 if (dst->role == dst->ROLE_SLIDER) | |
402 include_children = false; | |
403 | |
404 // Treat the active list box item as focused. | |
405 if (dst->role == dst->ROLE_LISTBOX_OPTION && src.isSelectedOptionActive()) | |
406 dst->state |= (1 << AccessibilityNodeData::STATE_FOCUSED); | |
407 | |
408 if (src.canvasHasFallbackContent()) | |
409 dst->role = AccessibilityNodeData::ROLE_CANVAS_WITH_FALLBACK_CONTENT; | |
410 | |
411 WebNode node = src.node(); | |
412 bool is_iframe = false; | |
413 | |
414 if (!node.isNull() && node.isElementNode()) { | |
415 WebElement element = node.to<WebElement>(); | |
416 is_iframe = (element.tagName() == ASCIIToUTF16("IFRAME")); | |
417 | |
418 if (LowerCaseEqualsASCII(element.getAttribute("aria-expanded"), "true")) | |
419 dst->state |= (1 << AccessibilityNodeData::STATE_EXPANDED); | |
420 | |
421 // TODO(ctguil): The tagName in WebKit is lower cased but | |
422 // HTMLElement::nodeName calls localNameUpper. Consider adding | |
423 // a WebElement method that returns the original lower cased tagName. | |
424 dst->string_attributes[dst->ATTR_HTML_TAG] = | |
425 StringToLowerASCII(string16(element.tagName())); | |
426 for (unsigned i = 0; i < element.attributeCount(); ++i) { | |
427 string16 name = StringToLowerASCII(string16( | |
428 element.attributeLocalName(i))); | |
429 string16 value = element.attributeValue(i); | |
430 dst->html_attributes.push_back( | |
431 std::pair<string16, string16>(name, value)); | |
432 } | |
433 | |
434 if (dst->role == dst->ROLE_EDITABLE_TEXT || | |
435 dst->role == dst->ROLE_TEXTAREA || | |
436 dst->role == dst->ROLE_TEXT_FIELD) { | |
437 // Jaws gets confused by children of text fields, so we ignore them. | |
438 include_children = false; | |
439 | |
440 dst->int_attributes[dst->ATTR_TEXT_SEL_START] = src.selectionStart(); | |
441 dst->int_attributes[dst->ATTR_TEXT_SEL_END] = src.selectionEnd(); | |
442 | |
443 WebVector<int> src_line_breaks; | |
444 src.lineBreaks(src_line_breaks); | |
445 dst->line_breaks.reserve(src_line_breaks.size()); | |
446 for (size_t i = 0; i < src_line_breaks.size(); ++i) | |
447 dst->line_breaks.push_back(src_line_breaks[i]); | |
448 } | |
449 | |
450 // ARIA role. | |
451 if (element.hasAttribute("role")) { | |
452 dst->string_attributes[dst->ATTR_ROLE] = element.getAttribute("role"); | |
453 } | |
454 | |
455 // Live region attributes | |
456 if (element.hasAttribute("aria-atomic")) { | |
457 dst->bool_attributes[dst->ATTR_LIVE_ATOMIC] = | |
458 LowerCaseEqualsASCII(element.getAttribute("aria-atomic"), "true"); | |
459 } | |
460 if (element.hasAttribute("aria-busy")) { | |
461 dst->bool_attributes[dst->ATTR_LIVE_BUSY] = | |
462 LowerCaseEqualsASCII(element.getAttribute("aria-busy"), "true"); | |
463 } | |
464 if (element.hasAttribute("aria-live")) { | |
465 dst->string_attributes[dst->ATTR_LIVE_STATUS] = | |
466 element.getAttribute("aria-live"); | |
467 } | |
468 if (element.hasAttribute("aria-relevant")) { | |
469 dst->string_attributes[dst->ATTR_LIVE_RELEVANT] = | |
470 element.getAttribute("aria-relevant"); | |
471 } | |
472 } | |
473 | |
474 // Walk up the parent chain to set live region attributes of containers | |
475 | |
476 WebAccessibilityObject container_accessible = src; | |
477 while (!container_accessible.isDetached()) { | |
478 WebNode container_node = container_accessible.node(); | |
479 if (!container_node.isNull() && container_node.isElementNode()) { | |
480 WebElement container_elem = | |
481 container_node.to<WebElement>(); | |
482 if (container_elem.hasAttribute("aria-atomic") && | |
483 dst->bool_attributes.find(dst->ATTR_CONTAINER_LIVE_ATOMIC) == | |
484 dst->bool_attributes.end()) { | |
485 dst->bool_attributes[dst->ATTR_CONTAINER_LIVE_ATOMIC] = | |
486 LowerCaseEqualsASCII(container_elem.getAttribute("aria-atomic"), | |
487 "true"); | |
488 } | |
489 if (container_elem.hasAttribute("aria-busy") && | |
490 dst->bool_attributes.find(dst->ATTR_CONTAINER_LIVE_BUSY) == | |
491 dst->bool_attributes.end()) { | |
492 dst->bool_attributes[dst->ATTR_CONTAINER_LIVE_BUSY] = | |
493 LowerCaseEqualsASCII(container_elem.getAttribute("aria-busy"), | |
494 "true"); | |
495 } | |
496 if (container_elem.hasAttribute("aria-live") && | |
497 dst->string_attributes.find(dst->ATTR_CONTAINER_LIVE_STATUS) == | |
498 dst->string_attributes.end()) { | |
499 dst->string_attributes[dst->ATTR_CONTAINER_LIVE_STATUS] = | |
500 container_elem.getAttribute("aria-live"); | |
501 } | |
502 if (container_elem.hasAttribute("aria-relevant") && | |
503 dst->string_attributes.find(dst->ATTR_CONTAINER_LIVE_RELEVANT) == | |
504 dst->string_attributes.end()) { | |
505 dst->string_attributes[dst->ATTR_CONTAINER_LIVE_RELEVANT] = | |
506 container_elem.getAttribute("aria-relevant"); | |
507 } | |
508 } | |
509 container_accessible = container_accessible.parentObject(); | |
510 } | |
511 | |
512 if (dst->role == dst->ROLE_PROGRESS_INDICATOR || | |
513 dst->role == dst->ROLE_SCROLLBAR || | |
514 dst->role == dst->ROLE_SLIDER || | |
515 dst->role == dst->ROLE_SPIN_BUTTON) { | |
516 dst->float_attributes[dst->ATTR_VALUE_FOR_RANGE] = src.valueForRange(); | |
517 dst->float_attributes[dst->ATTR_MAX_VALUE_FOR_RANGE] = | |
518 src.maxValueForRange(); | |
519 dst->float_attributes[dst->ATTR_MIN_VALUE_FOR_RANGE] = | |
520 src.minValueForRange(); | |
521 } | |
522 | |
523 if (dst->role == dst->ROLE_DOCUMENT || | |
524 dst->role == dst->ROLE_WEB_AREA) { | |
525 const WebDocument& document = src.document(); | |
526 if (dst->name.empty()) | |
527 dst->name = document.title(); | |
528 dst->string_attributes[dst->ATTR_DOC_TITLE] = document.title(); | |
529 dst->string_attributes[dst->ATTR_DOC_URL] = document.url().spec().utf16(); | |
530 dst->string_attributes[dst->ATTR_DOC_MIMETYPE] = | |
531 ASCIIToUTF16(document.isXHTMLDocument() ? "text/xhtml" : "text/html"); | |
532 dst->bool_attributes[dst->ATTR_DOC_LOADED] = src.isLoaded(); | |
533 dst->float_attributes[dst->ATTR_DOC_LOADING_PROGRESS] = | |
534 src.estimatedLoadingProgress(); | |
535 | |
536 const WebDocumentType& doctype = document.doctype(); | |
537 if (!doctype.isNull()) | |
538 dst->string_attributes[dst->ATTR_DOC_DOCTYPE] = doctype.name(); | |
539 | |
540 const gfx::Size& scroll_offset = document.frame()->scrollOffset(); | |
541 dst->int_attributes[dst->ATTR_SCROLL_X] = scroll_offset.width(); | |
542 dst->int_attributes[dst->ATTR_SCROLL_Y] = scroll_offset.height(); | |
543 | |
544 const gfx::Size& min_offset = document.frame()->minimumScrollOffset(); | |
545 dst->int_attributes[dst->ATTR_SCROLL_X_MIN] = min_offset.width(); | |
546 dst->int_attributes[dst->ATTR_SCROLL_Y_MIN] = min_offset.height(); | |
547 | |
548 const gfx::Size& max_offset = document.frame()->maximumScrollOffset(); | |
549 dst->int_attributes[dst->ATTR_SCROLL_X_MAX] = max_offset.width(); | |
550 dst->int_attributes[dst->ATTR_SCROLL_Y_MAX] = max_offset.height(); | |
551 } | |
552 | |
553 if (dst->role == dst->ROLE_TABLE) { | |
554 int column_count = src.columnCount(); | |
555 int row_count = src.rowCount(); | |
556 if (column_count > 0 && row_count > 0) { | |
557 std::set<int> unique_cell_id_set; | |
558 dst->int_attributes[dst->ATTR_TABLE_COLUMN_COUNT] = column_count; | |
559 dst->int_attributes[dst->ATTR_TABLE_ROW_COUNT] = row_count; | |
560 for (int i = 0; i < column_count * row_count; ++i) { | |
561 WebAccessibilityObject cell = src.cellForColumnAndRow( | |
562 i % column_count, i / column_count); | |
563 int cell_id = -1; | |
564 if (!cell.isDetached()) { | |
565 cell_id = cell.axID(); | |
566 if (unique_cell_id_set.find(cell_id) == unique_cell_id_set.end()) { | |
567 unique_cell_id_set.insert(cell_id); | |
568 dst->unique_cell_ids.push_back(cell_id); | |
569 } | |
570 } | |
571 dst->cell_ids.push_back(cell_id); | |
572 } | |
573 } | |
574 } | |
575 | |
576 if (dst->role == dst->ROLE_CELL || | |
577 dst->role == dst->ROLE_ROW_HEADER || | |
578 dst->role == dst->ROLE_COLUMN_HEADER) { | |
579 dst->int_attributes[dst->ATTR_TABLE_CELL_COLUMN_INDEX] = | |
580 src.cellColumnIndex(); | |
581 dst->int_attributes[dst->ATTR_TABLE_CELL_COLUMN_SPAN] = | |
582 src.cellColumnSpan(); | |
583 dst->int_attributes[dst->ATTR_TABLE_CELL_ROW_INDEX] = src.cellRowIndex(); | |
584 dst->int_attributes[dst->ATTR_TABLE_CELL_ROW_SPAN] = src.cellRowSpan(); | |
585 } | |
586 | |
587 if (include_children) { | |
588 // Recursively create children. | |
589 int child_count = src.childCount(); | |
590 std::set<int32> child_ids; | |
591 for (int i = 0; i < child_count; ++i) { | |
592 WebAccessibilityObject child = src.childAt(i); | |
593 int32 child_id = child.axID(); | |
594 | |
595 // The child may be invalid due to issues in webkit accessibility code. | |
596 // Don't add children that are invalid thus preventing a crash. | |
597 // https://bugs.webkit.org/show_bug.cgi?id=44149 | |
598 // TODO(ctguil): We may want to remove this check as webkit stabilizes. | |
599 if (child.isDetached()) | |
600 continue; | |
601 | |
602 // Children may duplicated in the webkit accessibility tree. Only add a | |
603 // child once for the web accessibility tree. | |
604 // https://bugs.webkit.org/show_bug.cgi?id=58930 | |
605 if (child_ids.find(child_id) != child_ids.end()) | |
606 continue; | |
607 child_ids.insert(child_id); | |
608 | |
609 // Some nodes appear in the tree in more than one place: for example, | |
610 // a cell in a table appears as a child of both a row and a column. | |
611 // Only recursively add child nodes that have this node as its | |
612 // unignored parent. For child nodes that are actually parented to | |
613 // somethinng else, store only the ID. | |
614 // | |
615 // As an exception, also add children of an iframe element. | |
616 // https://bugs.webkit.org/show_bug.cgi?id=57066 | |
617 if (is_iframe || IsParentUnignoredOf(src, child)) { | |
618 dst->children.push_back(AccessibilityNodeData()); | |
619 SerializeAccessibilityNode(child, | |
620 &dst->children.back(), | |
621 include_children); | |
622 } else { | |
623 dst->indirect_child_ids.push_back(child_id); | |
624 } | |
625 } | |
626 } | |
627 } | |
628 | |
629 } // namespace content | |
OLD | NEW |