[content shell] implement testRunner.overridePreference
[chromium-blink-merge.git] / content / renderer / accessibility_node_serializer.cc
blob076065c4a3f4a8da0670dfdbe283e1d3d6ee7080
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"
7 #include <set>
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;
34 namespace content {
35 namespace {
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) {
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::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;
265 default:
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) {
274 uint32 state = 0;
275 if (o.isChecked())
276 state |= (1 << AccessibilityNodeData::STATE_CHECKED);
278 if (o.isCollapsed())
279 state |= (1 << AccessibilityNodeData::STATE_COLLAPSED);
281 if (o.canSetFocusAttribute())
282 state |= (1 << AccessibilityNodeData::STATE_FOCUSABLE);
284 if (o.isFocused())
285 state |= (1 << AccessibilityNodeData::STATE_FOCUSED);
287 if (o.roleValue() == WebKit::WebAccessibilityRolePopUpButton ||
288 o.ariaHasPopup()) {
289 state |= (1 << AccessibilityNodeData::STATE_HASPOPUP);
290 if (!o.isCollapsed())
291 state |= (1 << AccessibilityNodeData::STATE_EXPANDED);
294 if (o.isHovered())
295 state |= (1 << AccessibilityNodeData::STATE_HOTTRACKED);
297 if (o.isIndeterminate())
298 state |= (1 << AccessibilityNodeData::STATE_INDETERMINATE);
300 if (!o.isVisible())
301 state |= (1 << AccessibilityNodeData::STATE_INVISIBLE);
303 if (o.isLinked())
304 state |= (1 << AccessibilityNodeData::STATE_LINKED);
306 if (o.isMultiSelectable())
307 state |= (1 << AccessibilityNodeData::STATE_MULTISELECTABLE);
309 if (o.isOffScreen())
310 state |= (1 << AccessibilityNodeData::STATE_OFFSCREEN);
312 if (o.isPressed())
313 state |= (1 << AccessibilityNodeData::STATE_PRESSED);
315 if (o.isPasswordField())
316 state |= (1 << AccessibilityNodeData::STATE_PROTECTED);
318 if (o.isReadOnly())
319 state |= (1 << AccessibilityNodeData::STATE_READONLY);
321 if (o.isRequired())
322 state |= (1 << AccessibilityNodeData::STATE_REQUIRED);
324 if (o.canSetSelectedAttribute())
325 state |= (1 << AccessibilityNodeData::STATE_SELECTABLE);
327 if (o.isSelected())
328 state |= (1 << AccessibilityNodeData::STATE_SELECTED);
330 if (o.isVisited())
331 state |= (1 << AccessibilityNodeData::STATE_TRAVERSED);
333 if (!o.isEnabled())
334 state |= (1 << AccessibilityNodeData::STATE_UNAVAILABLE);
336 if (o.isVertical())
337 state |= (1 << AccessibilityNodeData::STATE_VERTICAL);
339 if (o.isVisited())
340 state |= (1 << AccessibilityNodeData::STATE_VISITED);
342 return state;
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();
359 else
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]);
437 // ARIA role.
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"),
474 "true");
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"),
481 "true");
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);
550 int cell_id = -1;
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())
587 continue;
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())
593 continue;
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(),
608 include_children);
609 } else {
610 dst->indirect_child_ids.push_back(child_id);
616 } // namespace content