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.
5 #include "content/renderer/accessibility_node_serializer.h"
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/WebKit/chromium/public/WebAccessibilityObject.h"
13 #include "third_party/WebKit/Source/WebKit/chromium/public/WebAccessibilityRole.h"
14 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
15 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocumentType.h"
16 #include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h"
17 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFormControlElement.h"
18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebInputElement.h"
20 #include "third_party/WebKit/Source/WebKit/chromium/public/WebNode.h"
21 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebRect.h"
22 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebSize.h"
23 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
24 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebVector.h"
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
;
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
);
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
) {
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::WebAccessibilityRoleDefinitionListDefinition
:
93 return AccessibilityNodeData::ROLE_DEFINITION_LIST_DEFINITION
;
94 case WebKit::WebAccessibilityRoleDefinitionListTerm
:
95 return AccessibilityNodeData::ROLE_DEFINITION_LIST_TERM
;
96 case WebKit::WebAccessibilityRoleDirectory
:
97 return AccessibilityNodeData::ROLE_DIRECTORY
;
98 case WebKit::WebAccessibilityRoleDisclosureTriangle
:
99 return AccessibilityNodeData::ROLE_DISCLOSURE_TRIANGLE
;
100 case WebKit::WebAccessibilityRoleDiv
:
101 return AccessibilityNodeData::ROLE_DIV
;
102 case WebKit::WebAccessibilityRoleDocument
:
103 return AccessibilityNodeData::ROLE_DOCUMENT
;
104 case WebKit::WebAccessibilityRoleDocumentArticle
:
105 return AccessibilityNodeData::ROLE_ARTICLE
;
106 case WebKit::WebAccessibilityRoleDocumentMath
:
107 return AccessibilityNodeData::ROLE_MATH
;
108 case WebKit::WebAccessibilityRoleDocumentNote
:
109 return AccessibilityNodeData::ROLE_NOTE
;
110 case WebKit::WebAccessibilityRoleDocumentRegion
:
111 return AccessibilityNodeData::ROLE_REGION
;
112 case WebKit::WebAccessibilityRoleDrawer
:
113 return AccessibilityNodeData::ROLE_DRAWER
;
114 case WebKit::WebAccessibilityRoleEditableText
:
115 return AccessibilityNodeData::ROLE_EDITABLE_TEXT
;
116 case WebKit::WebAccessibilityRoleFooter
:
117 return AccessibilityNodeData::ROLE_FOOTER
;
118 case WebKit::WebAccessibilityRoleForm
:
119 return AccessibilityNodeData::ROLE_FORM
;
120 case WebKit::WebAccessibilityRoleGrid
:
121 return AccessibilityNodeData::ROLE_GRID
;
122 case WebKit::WebAccessibilityRoleGroup
:
123 return AccessibilityNodeData::ROLE_GROUP
;
124 case WebKit::WebAccessibilityRoleGrowArea
:
125 return AccessibilityNodeData::ROLE_GROW_AREA
;
126 case WebKit::WebAccessibilityRoleHeading
:
127 return AccessibilityNodeData::ROLE_HEADING
;
128 case WebKit::WebAccessibilityRoleHelpTag
:
129 return AccessibilityNodeData::ROLE_HELP_TAG
;
130 case WebKit::WebAccessibilityRoleHorizontalRule
:
131 return AccessibilityNodeData::ROLE_HORIZONTAL_RULE
;
132 case WebKit::WebAccessibilityRoleIgnored
:
133 return AccessibilityNodeData::ROLE_IGNORED
;
134 case WebKit::WebAccessibilityRoleImage
:
135 return AccessibilityNodeData::ROLE_IMAGE
;
136 case WebKit::WebAccessibilityRoleImageMap
:
137 return AccessibilityNodeData::ROLE_IMAGE_MAP
;
138 case WebKit::WebAccessibilityRoleImageMapLink
:
139 return AccessibilityNodeData::ROLE_IMAGE_MAP_LINK
;
140 case WebKit::WebAccessibilityRoleIncrementor
:
141 return AccessibilityNodeData::ROLE_INCREMENTOR
;
142 case WebKit::WebAccessibilityRoleLabel
:
143 return AccessibilityNodeData::ROLE_LABEL
;
144 case WebKit::WebAccessibilityRoleLandmarkApplication
:
145 return AccessibilityNodeData::ROLE_LANDMARK_APPLICATION
;
146 case WebKit::WebAccessibilityRoleLandmarkBanner
:
147 return AccessibilityNodeData::ROLE_LANDMARK_BANNER
;
148 case WebKit::WebAccessibilityRoleLandmarkComplementary
:
149 return AccessibilityNodeData::ROLE_LANDMARK_COMPLEMENTARY
;
150 case WebKit::WebAccessibilityRoleLandmarkContentInfo
:
151 return AccessibilityNodeData::ROLE_LANDMARK_CONTENTINFO
;
152 case WebKit::WebAccessibilityRoleLandmarkMain
:
153 return AccessibilityNodeData::ROLE_LANDMARK_MAIN
;
154 case WebKit::WebAccessibilityRoleLandmarkNavigation
:
155 return AccessibilityNodeData::ROLE_LANDMARK_NAVIGATION
;
156 case WebKit::WebAccessibilityRoleLandmarkSearch
:
157 return AccessibilityNodeData::ROLE_LANDMARK_SEARCH
;
158 case WebKit::WebAccessibilityRoleLink
:
159 return AccessibilityNodeData::ROLE_LINK
;
160 case WebKit::WebAccessibilityRoleList
:
161 return AccessibilityNodeData::ROLE_LIST
;
162 case WebKit::WebAccessibilityRoleListBox
:
163 return AccessibilityNodeData::ROLE_LISTBOX
;
164 case WebKit::WebAccessibilityRoleListBoxOption
:
165 return AccessibilityNodeData::ROLE_LISTBOX_OPTION
;
166 case WebKit::WebAccessibilityRoleListItem
:
167 return AccessibilityNodeData::ROLE_LIST_ITEM
;
168 case WebKit::WebAccessibilityRoleListMarker
:
169 return AccessibilityNodeData::ROLE_LIST_MARKER
;
170 case WebKit::WebAccessibilityRoleMatte
:
171 return AccessibilityNodeData::ROLE_MATTE
;
172 case WebKit::WebAccessibilityRoleMenu
:
173 return AccessibilityNodeData::ROLE_MENU
;
174 case WebKit::WebAccessibilityRoleMenuBar
:
175 return AccessibilityNodeData::ROLE_MENU_BAR
;
176 case WebKit::WebAccessibilityRoleMenuButton
:
177 return AccessibilityNodeData::ROLE_MENU_BUTTON
;
178 case WebKit::WebAccessibilityRoleMenuItem
:
179 return AccessibilityNodeData::ROLE_MENU_ITEM
;
180 case WebKit::WebAccessibilityRoleMenuListOption
:
181 return AccessibilityNodeData::ROLE_MENU_LIST_OPTION
;
182 case WebKit::WebAccessibilityRoleMenuListPopup
:
183 return AccessibilityNodeData::ROLE_MENU_LIST_POPUP
;
184 case WebKit::WebAccessibilityRoleOutline
:
185 return AccessibilityNodeData::ROLE_OUTLINE
;
186 case WebKit::WebAccessibilityRoleParagraph
:
187 return AccessibilityNodeData::ROLE_PARAGRAPH
;
188 case WebKit::WebAccessibilityRolePopUpButton
:
189 return AccessibilityNodeData::ROLE_POPUP_BUTTON
;
190 case WebKit::WebAccessibilityRolePresentational
:
191 return AccessibilityNodeData::ROLE_PRESENTATIONAL
;
192 case WebKit::WebAccessibilityRoleProgressIndicator
:
193 return AccessibilityNodeData::ROLE_PROGRESS_INDICATOR
;
194 case WebKit::WebAccessibilityRoleRadioButton
:
195 return AccessibilityNodeData::ROLE_RADIO_BUTTON
;
196 case WebKit::WebAccessibilityRoleRadioGroup
:
197 return AccessibilityNodeData::ROLE_RADIO_GROUP
;
198 case WebKit::WebAccessibilityRoleRow
:
199 return AccessibilityNodeData::ROLE_ROW
;
200 case WebKit::WebAccessibilityRoleRowHeader
:
201 return AccessibilityNodeData::ROLE_ROW_HEADER
;
202 case WebKit::WebAccessibilityRoleRuler
:
203 return AccessibilityNodeData::ROLE_RULER
;
204 case WebKit::WebAccessibilityRoleRulerMarker
:
205 return AccessibilityNodeData::ROLE_RULER_MARKER
;
206 case WebKit::WebAccessibilityRoleScrollArea
:
207 return AccessibilityNodeData::ROLE_SCROLLAREA
;
208 case WebKit::WebAccessibilityRoleScrollBar
:
209 return AccessibilityNodeData::ROLE_SCROLLBAR
;
210 case WebKit::WebAccessibilityRoleSheet
:
211 return AccessibilityNodeData::ROLE_SHEET
;
212 case WebKit::WebAccessibilityRoleSlider
:
213 return AccessibilityNodeData::ROLE_SLIDER
;
214 case WebKit::WebAccessibilityRoleSliderThumb
:
215 return AccessibilityNodeData::ROLE_SLIDER_THUMB
;
216 case WebKit::WebAccessibilityRoleSpinButton
:
217 return AccessibilityNodeData::ROLE_SPIN_BUTTON
;
218 case WebKit::WebAccessibilityRoleSpinButtonPart
:
219 return AccessibilityNodeData::ROLE_SPIN_BUTTON_PART
;
220 case WebKit::WebAccessibilityRoleSplitGroup
:
221 return AccessibilityNodeData::ROLE_SPLIT_GROUP
;
222 case WebKit::WebAccessibilityRoleSplitter
:
223 return AccessibilityNodeData::ROLE_SPLITTER
;
224 case WebKit::WebAccessibilityRoleStaticText
:
225 return AccessibilityNodeData::ROLE_STATIC_TEXT
;
226 case WebKit::WebAccessibilityRoleSystemWide
:
227 return AccessibilityNodeData::ROLE_SYSTEM_WIDE
;
228 case WebKit::WebAccessibilityRoleTab
:
229 return AccessibilityNodeData::ROLE_TAB
;
230 case WebKit::WebAccessibilityRoleTabGroup
:
231 return AccessibilityNodeData::ROLE_TAB_GROUP_UNUSED
;
232 case WebKit::WebAccessibilityRoleTabList
:
233 return AccessibilityNodeData::ROLE_TAB_LIST
;
234 case WebKit::WebAccessibilityRoleTabPanel
:
235 return AccessibilityNodeData::ROLE_TAB_PANEL
;
236 case WebKit::WebAccessibilityRoleTable
:
237 return AccessibilityNodeData::ROLE_TABLE
;
238 case WebKit::WebAccessibilityRoleTableHeaderContainer
:
239 return AccessibilityNodeData::ROLE_TABLE_HEADER_CONTAINER
;
240 case WebKit::WebAccessibilityRoleTextArea
:
241 return AccessibilityNodeData::ROLE_TEXTAREA
;
242 case WebKit::WebAccessibilityRoleTextField
:
243 return AccessibilityNodeData::ROLE_TEXT_FIELD
;
244 case WebKit::WebAccessibilityRoleToggleButton
:
245 return AccessibilityNodeData::ROLE_TOGGLE_BUTTON
;
246 case WebKit::WebAccessibilityRoleToolbar
:
247 return AccessibilityNodeData::ROLE_TOOLBAR
;
248 case WebKit::WebAccessibilityRoleTreeGrid
:
249 return AccessibilityNodeData::ROLE_TREE_GRID
;
250 case WebKit::WebAccessibilityRoleTreeItemRole
:
251 return AccessibilityNodeData::ROLE_TREE_ITEM
;
252 case WebKit::WebAccessibilityRoleTreeRole
:
253 return AccessibilityNodeData::ROLE_TREE
;
254 case WebKit::WebAccessibilityRoleUserInterfaceTooltip
:
255 return AccessibilityNodeData::ROLE_TOOLTIP
;
256 case WebKit::WebAccessibilityRoleValueIndicator
:
257 return AccessibilityNodeData::ROLE_VALUE_INDICATOR
;
258 case WebKit::WebAccessibilityRoleWebArea
:
259 return AccessibilityNodeData::ROLE_WEB_AREA
;
260 case WebKit::WebAccessibilityRoleWebCoreLink
:
261 return AccessibilityNodeData::ROLE_WEBCORE_LINK
;
262 case WebKit::WebAccessibilityRoleWindow
:
263 return AccessibilityNodeData::ROLE_WINDOW
;
266 return AccessibilityNodeData::ROLE_UNKNOWN
;
270 // Provides a conversion between the WebAccessibilityObject state
271 // accessors and a state bitmask that can be serialized and sent to the
272 // Browser process. Rare state are sent as boolean attributes instead.
273 uint32
ConvertState(const WebAccessibilityObject
& o
) {
276 state
|= (1 << AccessibilityNodeData::STATE_CHECKED
);
279 state
|= (1 << AccessibilityNodeData::STATE_COLLAPSED
);
281 if (o
.canSetFocusAttribute())
282 state
|= (1 << AccessibilityNodeData::STATE_FOCUSABLE
);
285 state
|= (1 << AccessibilityNodeData::STATE_FOCUSED
);
287 if (o
.roleValue() == WebKit::WebAccessibilityRolePopUpButton
||
289 state
|= (1 << AccessibilityNodeData::STATE_HASPOPUP
);
290 if (!o
.isCollapsed())
291 state
|= (1 << AccessibilityNodeData::STATE_EXPANDED
);
295 state
|= (1 << AccessibilityNodeData::STATE_HOTTRACKED
);
297 if (o
.isIndeterminate())
298 state
|= (1 << AccessibilityNodeData::STATE_INDETERMINATE
);
301 state
|= (1 << AccessibilityNodeData::STATE_INVISIBLE
);
304 state
|= (1 << AccessibilityNodeData::STATE_LINKED
);
306 if (o
.isMultiSelectable())
307 state
|= (1 << AccessibilityNodeData::STATE_MULTISELECTABLE
);
310 state
|= (1 << AccessibilityNodeData::STATE_OFFSCREEN
);
313 state
|= (1 << AccessibilityNodeData::STATE_PRESSED
);
315 if (o
.isPasswordField())
316 state
|= (1 << AccessibilityNodeData::STATE_PROTECTED
);
319 state
|= (1 << AccessibilityNodeData::STATE_READONLY
);
322 state
|= (1 << AccessibilityNodeData::STATE_REQUIRED
);
324 if (o
.canSetSelectedAttribute())
325 state
|= (1 << AccessibilityNodeData::STATE_SELECTABLE
);
328 state
|= (1 << AccessibilityNodeData::STATE_SELECTED
);
331 state
|= (1 << AccessibilityNodeData::STATE_TRAVERSED
);
334 state
|= (1 << AccessibilityNodeData::STATE_UNAVAILABLE
);
337 state
|= (1 << AccessibilityNodeData::STATE_VERTICAL
);
340 state
|= (1 << AccessibilityNodeData::STATE_VISITED
);
345 } // Anonymous namespace
347 void SerializeAccessibilityNode(
348 const WebAccessibilityObject
& src
,
349 AccessibilityNodeData
* dst
,
350 bool include_children
) {
351 dst
->name
= src
.title();
352 dst
->role
= ConvertRole(src
.roleValue());
353 dst
->state
= ConvertState(src
);
354 dst
->location
= src
.boundingBoxRect();
355 dst
->id
= src
.axID();
357 if (src
.valueDescription().length())
358 dst
->value
= src
.valueDescription();
360 dst
->value
= src
.stringValue();
362 if (src
.accessKey().length())
363 dst
->string_attributes
[dst
->ATTR_ACCESS_KEY
] = src
.accessKey();
364 if (src
.actionVerb().length())
365 dst
->string_attributes
[dst
->ATTR_ACTION
] = src
.actionVerb();
366 if (src
.isAriaReadOnly())
367 dst
->bool_attributes
[dst
->ATTR_ARIA_READONLY
] = true;
368 if (src
.isButtonStateMixed())
369 dst
->bool_attributes
[dst
->ATTR_BUTTON_MIXED
] = true;
370 if (src
.canSetValueAttribute())
371 dst
->bool_attributes
[dst
->ATTR_CAN_SET_VALUE
] = true;
372 if (src
.accessibilityDescription().length())
373 dst
->string_attributes
[dst
->ATTR_DESCRIPTION
] =
374 src
.accessibilityDescription();
375 if (src
.hasComputedStyle())
376 dst
->string_attributes
[dst
->ATTR_DISPLAY
] = src
.computedStyleDisplay();
377 if (src
.helpText().length())
378 dst
->string_attributes
[dst
->ATTR_HELP
] = src
.helpText();
379 if (src
.keyboardShortcut().length())
380 dst
->string_attributes
[dst
->ATTR_SHORTCUT
] = src
.keyboardShortcut();
381 if (!src
.titleUIElement().isDetached()) {
382 dst
->int_attributes
[dst
->ATTR_TITLE_UI_ELEMENT
] =
383 src
.titleUIElement().axID();
385 if (!src
.url().isEmpty())
386 dst
->string_attributes
[dst
->ATTR_URL
] = src
.url().spec().utf16();
388 if (dst
->role
== dst
->ROLE_TREE_ITEM
)
389 dst
->int_attributes
[dst
->ATTR_HIERARCHICAL_LEVEL
] = src
.hierarchicalLevel();
391 if (dst
->role
== dst
->ROLE_SLIDER
)
392 include_children
= false;
394 // Treat the active list box item as focused.
395 if (dst
->role
== dst
->ROLE_LISTBOX_OPTION
&& src
.isSelectedOptionActive())
396 dst
->state
|= (1 << AccessibilityNodeData::STATE_FOCUSED
);
398 if (src
.canvasHasFallbackContent())
399 dst
->role
= AccessibilityNodeData::ROLE_CANVAS_WITH_FALLBACK_CONTENT
;
401 WebNode node
= src
.node();
402 bool is_iframe
= false;
404 if (!node
.isNull() && node
.isElementNode()) {
405 WebElement element
= node
.to
<WebElement
>();
406 is_iframe
= (element
.tagName() == ASCIIToUTF16("IFRAME"));
408 // TODO(ctguil): The tagName in WebKit is lower cased but
409 // HTMLElement::nodeName calls localNameUpper. Consider adding
410 // a WebElement method that returns the original lower cased tagName.
411 dst
->string_attributes
[dst
->ATTR_HTML_TAG
] =
412 StringToLowerASCII(string16(element
.tagName()));
413 for (unsigned i
= 0; i
< element
.attributeCount(); ++i
) {
414 string16 name
= StringToLowerASCII(string16(
415 element
.attributeLocalName(i
)));
416 string16 value
= element
.attributeValue(i
);
417 dst
->html_attributes
.push_back(
418 std::pair
<string16
, string16
>(name
, value
));
421 if (dst
->role
== dst
->ROLE_EDITABLE_TEXT
||
422 dst
->role
== dst
->ROLE_TEXTAREA
||
423 dst
->role
== dst
->ROLE_TEXT_FIELD
) {
424 // Jaws gets confused by children of text fields, so we ignore them.
425 include_children
= false;
427 dst
->int_attributes
[dst
->ATTR_TEXT_SEL_START
] = src
.selectionStart();
428 dst
->int_attributes
[dst
->ATTR_TEXT_SEL_END
] = src
.selectionEnd();
430 WebVector
<int> src_line_breaks
;
431 src
.lineBreaks(src_line_breaks
);
432 dst
->line_breaks
.reserve(src_line_breaks
.size());
433 for (size_t i
= 0; i
< src_line_breaks
.size(); ++i
)
434 dst
->line_breaks
.push_back(src_line_breaks
[i
]);
438 if (element
.hasAttribute("role")) {
439 dst
->string_attributes
[dst
->ATTR_ROLE
] = element
.getAttribute("role");
442 // Live region attributes
443 if (element
.hasAttribute("aria-atomic")) {
444 dst
->bool_attributes
[dst
->ATTR_LIVE_ATOMIC
] =
445 LowerCaseEqualsASCII(element
.getAttribute("aria-atomic"), "true");
447 if (element
.hasAttribute("aria-busy")) {
448 dst
->bool_attributes
[dst
->ATTR_LIVE_BUSY
] =
449 LowerCaseEqualsASCII(element
.getAttribute("aria-busy"), "true");
451 if (element
.hasAttribute("aria-live")) {
452 dst
->string_attributes
[dst
->ATTR_LIVE_STATUS
] =
453 element
.getAttribute("aria-live");
455 if (element
.hasAttribute("aria-relevant")) {
456 dst
->string_attributes
[dst
->ATTR_LIVE_RELEVANT
] =
457 element
.getAttribute("aria-relevant");
461 // Walk up the parent chain to set live region attributes of containers
463 WebAccessibilityObject container_accessible
= src
;
464 while (!container_accessible
.isDetached()) {
465 WebNode container_node
= container_accessible
.node();
466 if (!container_node
.isNull() && container_node
.isElementNode()) {
467 WebElement container_elem
=
468 container_node
.to
<WebElement
>();
469 if (container_elem
.hasAttribute("aria-atomic") &&
470 dst
->bool_attributes
.find(dst
->ATTR_CONTAINER_LIVE_ATOMIC
) ==
471 dst
->bool_attributes
.end()) {
472 dst
->bool_attributes
[dst
->ATTR_CONTAINER_LIVE_ATOMIC
] =
473 LowerCaseEqualsASCII(container_elem
.getAttribute("aria-atomic"),
476 if (container_elem
.hasAttribute("aria-busy") &&
477 dst
->bool_attributes
.find(dst
->ATTR_CONTAINER_LIVE_BUSY
) ==
478 dst
->bool_attributes
.end()) {
479 dst
->bool_attributes
[dst
->ATTR_CONTAINER_LIVE_BUSY
] =
480 LowerCaseEqualsASCII(container_elem
.getAttribute("aria-busy"),
483 if (container_elem
.hasAttribute("aria-live") &&
484 dst
->string_attributes
.find(dst
->ATTR_CONTAINER_LIVE_STATUS
) ==
485 dst
->string_attributes
.end()) {
486 dst
->string_attributes
[dst
->ATTR_CONTAINER_LIVE_STATUS
] =
487 container_elem
.getAttribute("aria-live");
489 if (container_elem
.hasAttribute("aria-relevant") &&
490 dst
->string_attributes
.find(dst
->ATTR_CONTAINER_LIVE_RELEVANT
) ==
491 dst
->string_attributes
.end()) {
492 dst
->string_attributes
[dst
->ATTR_CONTAINER_LIVE_RELEVANT
] =
493 container_elem
.getAttribute("aria-relevant");
496 container_accessible
= container_accessible
.parentObject();
499 if (dst
->role
== dst
->ROLE_PROGRESS_INDICATOR
||
500 dst
->role
== dst
->ROLE_SCROLLBAR
||
501 dst
->role
== dst
->ROLE_SLIDER
||
502 dst
->role
== dst
->ROLE_SPIN_BUTTON
) {
503 dst
->float_attributes
[dst
->ATTR_VALUE_FOR_RANGE
] = src
.valueForRange();
504 dst
->float_attributes
[dst
->ATTR_MAX_VALUE_FOR_RANGE
] =
505 src
.minValueForRange();
506 dst
->float_attributes
[dst
->ATTR_MIN_VALUE_FOR_RANGE
] =
507 src
.maxValueForRange();
510 if (dst
->role
== dst
->ROLE_DOCUMENT
||
511 dst
->role
== dst
->ROLE_WEB_AREA
) {
512 const WebDocument
& document
= src
.document();
513 if (dst
->name
.empty())
514 dst
->name
= document
.title();
515 dst
->string_attributes
[dst
->ATTR_DOC_TITLE
] = document
.title();
516 dst
->string_attributes
[dst
->ATTR_DOC_URL
] = document
.url().spec().utf16();
517 dst
->string_attributes
[dst
->ATTR_DOC_MIMETYPE
] =
518 ASCIIToUTF16(document
.isXHTMLDocument() ? "text/xhtml" : "text/html");
519 dst
->bool_attributes
[dst
->ATTR_DOC_LOADED
] = src
.isLoaded();
520 dst
->float_attributes
[dst
->ATTR_DOC_LOADING_PROGRESS
] =
521 src
.estimatedLoadingProgress();
523 const WebDocumentType
& doctype
= document
.doctype();
524 if (!doctype
.isNull())
525 dst
->string_attributes
[dst
->ATTR_DOC_DOCTYPE
] = doctype
.name();
527 const gfx::Size
& scroll_offset
= document
.frame()->scrollOffset();
528 dst
->int_attributes
[dst
->ATTR_SCROLL_X
] = scroll_offset
.width();
529 dst
->int_attributes
[dst
->ATTR_SCROLL_Y
] = scroll_offset
.height();
531 const gfx::Size
& min_offset
= document
.frame()->minimumScrollOffset();
532 dst
->int_attributes
[dst
->ATTR_SCROLL_X_MIN
] = min_offset
.width();
533 dst
->int_attributes
[dst
->ATTR_SCROLL_Y_MIN
] = min_offset
.height();
535 const gfx::Size
& max_offset
= document
.frame()->maximumScrollOffset();
536 dst
->int_attributes
[dst
->ATTR_SCROLL_X_MAX
] = max_offset
.width();
537 dst
->int_attributes
[dst
->ATTR_SCROLL_Y_MAX
] = max_offset
.height();
540 if (dst
->role
== dst
->ROLE_TABLE
) {
541 int column_count
= src
.columnCount();
542 int row_count
= src
.rowCount();
543 if (column_count
> 0 && row_count
> 0) {
544 std::set
<int> unique_cell_id_set
;
545 dst
->int_attributes
[dst
->ATTR_TABLE_COLUMN_COUNT
] = column_count
;
546 dst
->int_attributes
[dst
->ATTR_TABLE_ROW_COUNT
] = row_count
;
547 for (int i
= 0; i
< column_count
* row_count
; ++i
) {
548 WebAccessibilityObject cell
= src
.cellForColumnAndRow(
549 i
% column_count
, i
/ column_count
);
551 if (!cell
.isDetached()) {
552 cell_id
= cell
.axID();
553 if (unique_cell_id_set
.find(cell_id
) == unique_cell_id_set
.end()) {
554 unique_cell_id_set
.insert(cell_id
);
555 dst
->unique_cell_ids
.push_back(cell_id
);
558 dst
->cell_ids
.push_back(cell_id
);
563 if (dst
->role
== dst
->ROLE_CELL
||
564 dst
->role
== dst
->ROLE_ROW_HEADER
||
565 dst
->role
== dst
->ROLE_COLUMN_HEADER
) {
566 dst
->int_attributes
[dst
->ATTR_TABLE_CELL_COLUMN_INDEX
] =
567 src
.cellColumnIndex();
568 dst
->int_attributes
[dst
->ATTR_TABLE_CELL_COLUMN_SPAN
] =
569 src
.cellColumnSpan();
570 dst
->int_attributes
[dst
->ATTR_TABLE_CELL_ROW_INDEX
] = src
.cellRowIndex();
571 dst
->int_attributes
[dst
->ATTR_TABLE_CELL_ROW_SPAN
] = src
.cellRowSpan();
574 if (include_children
) {
575 // Recursively create children.
576 int child_count
= src
.childCount();
577 std::set
<int32
> child_ids
;
578 for (int i
= 0; i
< child_count
; ++i
) {
579 WebAccessibilityObject child
= src
.childAt(i
);
580 int32 child_id
= child
.axID();
582 // The child may be invalid due to issues in webkit accessibility code.
583 // Don't add children that are invalid thus preventing a crash.
584 // https://bugs.webkit.org/show_bug.cgi?id=44149
585 // TODO(ctguil): We may want to remove this check as webkit stabilizes.
586 if (child
.isDetached())
589 // Children may duplicated in the webkit accessibility tree. Only add a
590 // child once for the web accessibility tree.
591 // https://bugs.webkit.org/show_bug.cgi?id=58930
592 if (child_ids
.find(child_id
) != child_ids
.end())
594 child_ids
.insert(child_id
);
596 // Some nodes appear in the tree in more than one place: for example,
597 // a cell in a table appears as a child of both a row and a column.
598 // Only recursively add child nodes that have this node as its
599 // unignored parent. For child nodes that are actually parented to
600 // somethinng else, store only the ID.
602 // As an exception, also add children of an iframe element.
603 // https://bugs.webkit.org/show_bug.cgi?id=57066
604 if (is_iframe
|| IsParentUnignoredOf(src
, child
)) {
605 dst
->children
.push_back(AccessibilityNodeData());
606 SerializeAccessibilityNode(child
,
607 &dst
->children
.back(),
610 dst
->indirect_child_ids
.push_back(child_id
);
616 } // namespace content