Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / content / renderer / accessibility / blink_ax_tree_source.cc
blobe72cd6e91faeca13d158ee5c992f0b9167b9fc9e
1 // Copyright 2014 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/blink_ax_tree_source.h"
7 #include <set>
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "content/renderer/accessibility/blink_ax_enum_conversion.h"
13 #include "content/renderer/browser_plugin/browser_plugin.h"
14 #include "content/renderer/render_frame_impl.h"
15 #include "content/renderer/render_frame_proxy.h"
16 #include "content/renderer/render_view_impl.h"
17 #include "third_party/WebKit/public/platform/WebRect.h"
18 #include "third_party/WebKit/public/platform/WebSize.h"
19 #include "third_party/WebKit/public/platform/WebString.h"
20 #include "third_party/WebKit/public/platform/WebVector.h"
21 #include "third_party/WebKit/public/web/WebAXEnums.h"
22 #include "third_party/WebKit/public/web/WebAXObject.h"
23 #include "third_party/WebKit/public/web/WebDocument.h"
24 #include "third_party/WebKit/public/web/WebDocumentType.h"
25 #include "third_party/WebKit/public/web/WebElement.h"
26 #include "third_party/WebKit/public/web/WebFormControlElement.h"
27 #include "third_party/WebKit/public/web/WebFrame.h"
28 #include "third_party/WebKit/public/web/WebLocalFrame.h"
29 #include "third_party/WebKit/public/web/WebNode.h"
30 #include "third_party/WebKit/public/web/WebPlugin.h"
31 #include "third_party/WebKit/public/web/WebPluginContainer.h"
32 #include "third_party/WebKit/public/web/WebView.h"
34 using base::ASCIIToUTF16;
35 using base::UTF16ToUTF8;
36 using blink::WebAXObject;
37 using blink::WebDocument;
38 using blink::WebDocumentType;
39 using blink::WebElement;
40 using blink::WebFrame;
41 using blink::WebNode;
42 using blink::WebPlugin;
43 using blink::WebPluginContainer;
44 using blink::WebVector;
45 using blink::WebView;
47 namespace content {
49 namespace {
51 // Returns true if |ancestor| is the first unignored parent of |child|,
52 // which means that when walking up the parent chain from |child|,
53 // |ancestor| is the *first* ancestor that isn't marked as
54 // accessibilityIsIgnored().
55 bool IsParentUnignoredOf(WebAXObject ancestor,
56 WebAXObject child) {
57 WebAXObject parent = child.parentObject();
58 while (!parent.isDetached() && parent.accessibilityIsIgnored())
59 parent = parent.parentObject();
60 return parent.equals(ancestor);
63 std::string GetEquivalentAriaRoleString(const ui::AXRole role) {
64 switch (role) {
65 case ui::AX_ROLE_ARTICLE:
66 return "article";
67 case ui::AX_ROLE_BANNER:
68 return "banner";
69 case ui::AX_ROLE_BUTTON:
70 return "button";
71 case ui::AX_ROLE_COMPLEMENTARY:
72 return "complementary";
73 case ui::AX_ROLE_FIGURE:
74 return "figure";
75 case ui::AX_ROLE_FOOTER:
76 return "contentinfo";
77 case ui::AX_ROLE_HEADING:
78 return "heading";
79 case ui::AX_ROLE_IMAGE:
80 return "img";
81 case ui::AX_ROLE_MAIN:
82 return "main";
83 case ui::AX_ROLE_NAVIGATION:
84 return "navigation";
85 case ui::AX_ROLE_RADIO_BUTTON:
86 return "radio";
87 case ui::AX_ROLE_REGION:
88 return "region";
89 case ui::AX_ROLE_SLIDER:
90 return "slider";
91 default:
92 break;
95 return std::string();
98 void AddIntListAttributeFromWebObjects(ui::AXIntListAttribute attr,
99 WebVector<WebAXObject> objects,
100 ui::AXNodeData* dst) {
101 std::vector<int32> ids;
102 for(size_t i = 0; i < objects.size(); i++)
103 ids.push_back(objects[i].axID());
104 if (ids.size() > 0)
105 dst->AddIntListAttribute(attr, ids);
108 } // Anonymous namespace
110 BlinkAXTreeSource::BlinkAXTreeSource(RenderFrameImpl* render_frame)
111 : render_frame_(render_frame),
112 node_to_frame_routing_id_map_(NULL),
113 node_to_browser_plugin_instance_id_map_(NULL),
114 accessibility_focus_id_(-1) {
117 BlinkAXTreeSource::~BlinkAXTreeSource() {
120 void BlinkAXTreeSource::SetRoot(blink::WebAXObject root) {
121 root_ = root;
124 bool BlinkAXTreeSource::IsInTree(blink::WebAXObject node) const {
125 const blink::WebAXObject& root = GetRoot();
126 while (IsValid(node)) {
127 if (node.equals(root))
128 return true;
129 node = GetParent(node);
131 return false;
134 void BlinkAXTreeSource::CollectChildFrameIdMapping(
135 std::map<int32, int>* node_to_frame_routing_id_map,
136 std::map<int32, int>* node_to_browser_plugin_instance_id_map) {
137 node_to_frame_routing_id_map_ = node_to_frame_routing_id_map;
138 node_to_browser_plugin_instance_id_map_ =
139 node_to_browser_plugin_instance_id_map;
142 blink::WebAXObject BlinkAXTreeSource::GetRoot() const {
143 if (!root_.isNull())
144 return root_;
145 return GetMainDocument().accessibilityObject();
148 blink::WebAXObject BlinkAXTreeSource::GetFromId(int32 id) const {
149 return GetMainDocument().accessibilityObjectFromID(id);
152 int32 BlinkAXTreeSource::GetId(blink::WebAXObject node) const {
153 return node.axID();
156 void BlinkAXTreeSource::GetChildren(
157 blink::WebAXObject parent,
158 std::vector<blink::WebAXObject>* out_children) const {
159 if (parent.role() == blink::WebAXRoleStaticText) {
160 blink::WebAXObject ancestor = parent;
161 while (!ancestor.isDetached()) {
162 if (ancestor.axID() == accessibility_focus_id_) {
163 parent.loadInlineTextBoxes();
164 break;
166 ancestor = ancestor.parentObject();
170 bool is_iframe = false;
171 WebNode node = parent.node();
172 if (!node.isNull() && node.isElementNode()) {
173 WebElement element = node.to<WebElement>();
174 is_iframe = (element.tagName() == ASCIIToUTF16("IFRAME"));
177 for (unsigned i = 0; i < parent.childCount(); i++) {
178 blink::WebAXObject child = parent.childAt(i);
180 // The child may be invalid due to issues in blink accessibility code.
181 if (child.isDetached())
182 continue;
184 // Skip children whose parent isn't |parent|.
185 // As an exception, include children of an iframe element.
186 if (!is_iframe && !IsParentUnignoredOf(parent, child))
187 continue;
189 out_children->push_back(child);
193 blink::WebAXObject BlinkAXTreeSource::GetParent(
194 blink::WebAXObject node) const {
195 // Blink returns ignored objects when walking up the parent chain,
196 // we have to skip those here. Also, stop when we get to the root
197 // element.
198 blink::WebAXObject root = GetRoot();
199 do {
200 if (node.equals(root))
201 return blink::WebAXObject();
202 node = node.parentObject();
203 } while (!node.isDetached() && node.accessibilityIsIgnored());
205 return node;
208 bool BlinkAXTreeSource::IsValid(blink::WebAXObject node) const {
209 return !node.isDetached(); // This also checks if it's null.
212 bool BlinkAXTreeSource::IsEqual(blink::WebAXObject node1,
213 blink::WebAXObject node2) const {
214 return node1.equals(node2);
217 blink::WebAXObject BlinkAXTreeSource::GetNull() const {
218 return blink::WebAXObject();
221 void BlinkAXTreeSource::SerializeNode(blink::WebAXObject src,
222 ui::AXNodeData* dst) const {
223 dst->role = AXRoleFromBlink(src.role());
224 dst->state = AXStateFromBlink(src);
225 dst->location = src.boundingBoxRect();
226 dst->id = src.axID();
227 std::string name = UTF16ToUTF8(base::StringPiece16(src.deprecatedTitle()));
229 std::string value;
230 if (src.valueDescription().length()) {
231 dst->AddStringAttribute(ui::AX_ATTR_VALUE,
232 UTF16ToUTF8(base::StringPiece16(
233 src.valueDescription())));
234 } else {
235 dst->AddStringAttribute(
236 ui::AX_ATTR_VALUE,
237 UTF16ToUTF8(base::StringPiece16(src.stringValue())));
240 if (dst->role == ui::AX_ROLE_COLOR_WELL)
241 dst->AddIntAttribute(ui::AX_ATTR_COLOR_VALUE, src.colorValue());
244 // Text attributes.
245 if (src.backgroundColor())
246 dst->AddIntAttribute(ui::AX_ATTR_BACKGROUND_COLOR, src.backgroundColor());
248 if (src.color())
249 dst->AddIntAttribute(ui::AX_ATTR_COLOR, src.color());
251 // Font size is in pixels.
252 if (src.fontSize())
253 dst->AddFloatAttribute(ui::AX_ATTR_FONT_SIZE, src.fontSize());
255 if (src.invalidState()) {
256 dst->AddIntAttribute(ui::AX_ATTR_INVALID_STATE,
257 AXInvalidStateFromBlink(src.invalidState()));
259 if (src.invalidState() == blink::WebAXInvalidStateOther) {
260 dst->AddStringAttribute(
261 ui::AX_ATTR_ARIA_INVALID_VALUE,
262 UTF16ToUTF8(base::StringPiece16(src.ariaInvalidValue())));
265 if (src.textDirection()) {
266 dst->AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
267 AXTextDirectionFromBlink(src.textDirection()));
270 if (src.textStyle()) {
271 dst->AddIntAttribute(ui::AX_ATTR_TEXT_STYLE,
272 AXTextStyleFromBlink(src.textStyle()));
276 if (dst->role == ui::AX_ROLE_INLINE_TEXT_BOX) {
277 WebVector<int> src_character_offsets;
278 src.characterOffsets(src_character_offsets);
279 std::vector<int32> character_offsets;
280 character_offsets.reserve(src_character_offsets.size());
281 for (size_t i = 0; i < src_character_offsets.size(); ++i)
282 character_offsets.push_back(src_character_offsets[i]);
283 dst->AddIntListAttribute(ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets);
285 WebVector<int> src_word_starts;
286 WebVector<int> src_word_ends;
287 src.wordBoundaries(src_word_starts, src_word_ends);
288 std::vector<int32> word_starts;
289 std::vector<int32> word_ends;
290 word_starts.reserve(src_word_starts.size());
291 word_ends.reserve(src_word_starts.size());
292 for (size_t i = 0; i < src_word_starts.size(); ++i) {
293 word_starts.push_back(src_word_starts[i]);
294 word_ends.push_back(src_word_ends[i]);
296 dst->AddIntListAttribute(ui::AX_ATTR_WORD_STARTS, word_starts);
297 dst->AddIntListAttribute(ui::AX_ATTR_WORD_ENDS, word_ends);
300 if (src.accessKey().length()) {
301 dst->AddStringAttribute(ui::AX_ATTR_ACCESS_KEY,
302 UTF16ToUTF8(base::StringPiece16(src.accessKey())));
305 if (src.actionVerb().length())
306 dst->AddStringAttribute(
307 ui::AX_ATTR_ACTION,
308 UTF16ToUTF8(base::StringPiece16(src.actionVerb())));
309 if (src.ariaAutoComplete().length())
310 dst->AddStringAttribute(
311 ui::AX_ATTR_AUTO_COMPLETE,
312 UTF16ToUTF8(base::StringPiece16(src.ariaAutoComplete())));
313 if (src.isAriaReadOnly())
314 dst->AddBoolAttribute(ui::AX_ATTR_ARIA_READONLY, true);
315 if (src.isButtonStateMixed())
316 dst->AddBoolAttribute(ui::AX_ATTR_BUTTON_MIXED, true);
317 if (src.canSetValueAttribute())
318 dst->AddBoolAttribute(ui::AX_ATTR_CAN_SET_VALUE, true);
319 if (src.deprecatedAccessibilityDescription().length()) {
320 dst->AddStringAttribute(
321 ui::AX_ATTR_DESCRIPTION,
322 UTF16ToUTF8(base::StringPiece16(
323 src.deprecatedAccessibilityDescription())));
325 if (src.hasComputedStyle()) {
326 dst->AddStringAttribute(
327 ui::AX_ATTR_DISPLAY,
328 UTF16ToUTF8(base::StringPiece16(src.computedStyleDisplay())));
330 if (src.deprecatedHelpText().length())
331 dst->AddStringAttribute(
332 ui::AX_ATTR_HELP,
333 UTF16ToUTF8(base::StringPiece16((src.deprecatedHelpText()))));
334 if (src.deprecatedPlaceholder().length()) {
335 dst->AddStringAttribute(
336 ui::AX_ATTR_PLACEHOLDER,
337 UTF16ToUTF8(base::StringPiece16(src.deprecatedPlaceholder())));
339 if (src.keyboardShortcut().length()) {
340 dst->AddStringAttribute(
341 ui::AX_ATTR_SHORTCUT,
342 UTF16ToUTF8(base::StringPiece16(src.keyboardShortcut())));
344 if (!src.deprecatedTitleUIElement().isDetached()) {
345 dst->AddIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT,
346 src.deprecatedTitleUIElement().axID());
348 if (!src.ariaActiveDescendant().isDetached()) {
349 dst->AddIntAttribute(ui::AX_ATTR_ACTIVEDESCENDANT_ID,
350 src.ariaActiveDescendant().axID());
353 if (!src.url().isEmpty())
354 dst->AddStringAttribute(ui::AX_ATTR_URL, src.url().spec());
356 if (dst->role == ui::AX_ROLE_HEADING)
357 dst->AddIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL, src.headingLevel());
358 else if ((dst->role == ui::AX_ROLE_TREE_ITEM ||
359 dst->role == ui::AX_ROLE_ROW) &&
360 src.hierarchicalLevel() > 0) {
361 dst->AddIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL,
362 src.hierarchicalLevel());
365 if (src.setSize())
366 dst->AddIntAttribute(ui::AX_ATTR_SET_SIZE, src.setSize());
368 if (src.posInSet())
369 dst->AddIntAttribute(ui::AX_ATTR_POS_IN_SET, src.posInSet());
371 // Treat the active list box item as focused.
372 if (dst->role == ui::AX_ROLE_LIST_BOX_OPTION &&
373 src.isSelectedOptionActive()) {
374 dst->state |= (1 << ui::AX_STATE_FOCUSED);
377 if (src.canvasHasFallbackContent())
378 dst->AddBoolAttribute(ui::AX_ATTR_CANVAS_HAS_FALLBACK, true);
380 WebNode node = src.node();
381 bool is_iframe = false;
383 if (!node.isNull() && node.isElementNode()) {
384 WebElement element = node.to<WebElement>();
385 is_iframe = (element.tagName() == ASCIIToUTF16("IFRAME"));
387 // TODO(ctguil): The tagName in WebKit is lower cased but
388 // HTMLElement::nodeName calls localNameUpper. Consider adding
389 // a WebElement method that returns the original lower cased tagName.
390 dst->AddStringAttribute(
391 ui::AX_ATTR_HTML_TAG,
392 base::StringToLowerASCII(UTF16ToUTF8(
393 base::StringPiece16(element.tagName()))));
394 for (unsigned i = 0; i < element.attributeCount(); ++i) {
395 std::string name = base::StringToLowerASCII(UTF16ToUTF8(
396 base::StringPiece16(element.attributeLocalName(i))));
397 std::string value =
398 UTF16ToUTF8(base::StringPiece16(element.attributeValue(i)));
399 dst->html_attributes.push_back(std::make_pair(name, value));
402 if (!src.isReadOnly() || dst->role == ui::AX_ROLE_TEXT_FIELD) {
403 dst->AddIntAttribute(ui::AX_ATTR_TEXT_SEL_START, src.selectionStart());
404 dst->AddIntAttribute(ui::AX_ATTR_TEXT_SEL_END, src.selectionEnd());
406 WebVector<int> src_line_breaks;
407 src.lineBreaks(src_line_breaks);
408 if (src_line_breaks.size() > 0) {
409 std::vector<int32> line_breaks;
410 line_breaks.reserve(src_line_breaks.size());
411 for (size_t i = 0; i < src_line_breaks.size(); ++i)
412 line_breaks.push_back(src_line_breaks[i]);
413 dst->AddIntListAttribute(ui::AX_ATTR_LINE_BREAKS, line_breaks);
417 // ARIA role.
418 if (element.hasAttribute("role")) {
419 dst->AddStringAttribute(
420 ui::AX_ATTR_ROLE,
421 UTF16ToUTF8(base::StringPiece16(element.getAttribute("role"))));
422 } else {
423 std::string role = GetEquivalentAriaRoleString(dst->role);
424 if (!role.empty())
425 dst->AddStringAttribute(ui::AX_ATTR_ROLE, role);
426 else if (dst->role == ui::AX_ROLE_TIME)
427 dst->AddStringAttribute(ui::AX_ATTR_ROLE, "time");
430 // Browser plugin (used in a <webview>).
431 if (node_to_browser_plugin_instance_id_map_) {
432 BrowserPlugin* browser_plugin = BrowserPlugin::GetFromNode(element);
433 if (browser_plugin) {
434 (*node_to_browser_plugin_instance_id_map_)[dst->id] =
435 browser_plugin->browser_plugin_instance_id();
436 dst->AddBoolAttribute(ui::AX_ATTR_IS_AX_TREE_HOST, true);
440 // Out-of-process iframe.
441 if (is_iframe && node_to_frame_routing_id_map_) {
442 WebFrame* frame = WebFrame::fromFrameOwnerElement(element);
444 if (frame && frame->isWebRemoteFrame()) {
445 RenderFrameProxy* render_frame_proxy =
446 RenderFrameProxy::FromWebFrame(frame);
448 DCHECK(render_frame_proxy);
449 (*node_to_frame_routing_id_map_)[dst->id] =
450 render_frame_proxy->routing_id();
451 dst->AddBoolAttribute(ui::AX_ATTR_IS_AX_TREE_HOST, true);
456 if (src.isInLiveRegion()) {
457 dst->AddBoolAttribute(ui::AX_ATTR_LIVE_ATOMIC, src.liveRegionAtomic());
458 dst->AddBoolAttribute(ui::AX_ATTR_LIVE_BUSY, src.liveRegionBusy());
459 if (src.liveRegionBusy())
460 dst->state |= (1 << ui::AX_STATE_BUSY);
461 if (!src.liveRegionStatus().isEmpty()) {
462 dst->AddStringAttribute(
463 ui::AX_ATTR_LIVE_STATUS,
464 UTF16ToUTF8(base::StringPiece16(src.liveRegionStatus())));
466 dst->AddStringAttribute(
467 ui::AX_ATTR_LIVE_RELEVANT,
468 UTF16ToUTF8(base::StringPiece16(src.liveRegionRelevant())));
469 dst->AddBoolAttribute(ui::AX_ATTR_CONTAINER_LIVE_ATOMIC,
470 src.containerLiveRegionAtomic());
471 dst->AddBoolAttribute(ui::AX_ATTR_CONTAINER_LIVE_BUSY,
472 src.containerLiveRegionBusy());
473 dst->AddStringAttribute(
474 ui::AX_ATTR_CONTAINER_LIVE_STATUS,
475 UTF16ToUTF8(base::StringPiece16(src.containerLiveRegionStatus())));
476 dst->AddStringAttribute(
477 ui::AX_ATTR_CONTAINER_LIVE_RELEVANT,
478 UTF16ToUTF8(base::StringPiece16(src.containerLiveRegionRelevant())));
481 if (dst->role == ui::AX_ROLE_PROGRESS_INDICATOR ||
482 dst->role == ui::AX_ROLE_METER ||
483 dst->role == ui::AX_ROLE_SCROLL_BAR ||
484 dst->role == ui::AX_ROLE_SLIDER ||
485 dst->role == ui::AX_ROLE_SPIN_BUTTON) {
486 dst->AddFloatAttribute(ui::AX_ATTR_VALUE_FOR_RANGE, src.valueForRange());
487 dst->AddFloatAttribute(ui::AX_ATTR_MAX_VALUE_FOR_RANGE,
488 src.maxValueForRange());
489 dst->AddFloatAttribute(ui::AX_ATTR_MIN_VALUE_FOR_RANGE,
490 src.minValueForRange());
493 if (dst->role == ui::AX_ROLE_WEB_AREA) {
494 dst->AddStringAttribute(ui::AX_ATTR_HTML_TAG, "#document");
495 const WebDocument& document = src.document();
496 if (name.empty())
497 name = UTF16ToUTF8(base::StringPiece16(document.title()));
498 dst->AddStringAttribute(
499 ui::AX_ATTR_DOC_TITLE,
500 UTF16ToUTF8(base::StringPiece16(document.title())));
501 dst->AddStringAttribute(ui::AX_ATTR_DOC_URL, document.url().spec());
502 dst->AddStringAttribute(
503 ui::AX_ATTR_DOC_MIMETYPE,
504 document.isXHTMLDocument() ? "text/xhtml" : "text/html");
505 dst->AddBoolAttribute(ui::AX_ATTR_DOC_LOADED, src.isLoaded());
506 dst->AddFloatAttribute(ui::AX_ATTR_DOC_LOADING_PROGRESS,
507 src.estimatedLoadingProgress());
509 const WebDocumentType& doctype = document.doctype();
510 if (!doctype.isNull()) {
511 dst->AddStringAttribute(
512 ui::AX_ATTR_DOC_DOCTYPE,
513 UTF16ToUTF8(base::StringPiece16(doctype.name())));
518 if (dst->role == ui::AX_ROLE_TABLE) {
519 int column_count = src.columnCount();
520 int row_count = src.rowCount();
521 if (column_count > 0 && row_count > 0) {
522 std::set<int32> unique_cell_id_set;
523 std::vector<int32> cell_ids;
524 std::vector<int32> unique_cell_ids;
525 dst->AddIntAttribute(ui::AX_ATTR_TABLE_COLUMN_COUNT, column_count);
526 dst->AddIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, row_count);
527 WebAXObject header = src.headerContainerObject();
528 if (!header.isDetached())
529 dst->AddIntAttribute(ui::AX_ATTR_TABLE_HEADER_ID, header.axID());
530 for (int i = 0; i < column_count * row_count; ++i) {
531 WebAXObject cell = src.cellForColumnAndRow(
532 i % column_count, i / column_count);
533 int cell_id = -1;
534 if (!cell.isDetached()) {
535 cell_id = cell.axID();
536 if (unique_cell_id_set.find(cell_id) == unique_cell_id_set.end()) {
537 unique_cell_id_set.insert(cell_id);
538 unique_cell_ids.push_back(cell_id);
541 cell_ids.push_back(cell_id);
543 dst->AddIntListAttribute(ui::AX_ATTR_CELL_IDS, cell_ids);
544 dst->AddIntListAttribute(ui::AX_ATTR_UNIQUE_CELL_IDS, unique_cell_ids);
548 if (dst->role == ui::AX_ROLE_ROW) {
549 dst->AddIntAttribute(ui::AX_ATTR_TABLE_ROW_INDEX, src.rowIndex());
550 WebAXObject header = src.rowHeader();
551 if (!header.isDetached())
552 dst->AddIntAttribute(ui::AX_ATTR_TABLE_ROW_HEADER_ID, header.axID());
555 if (dst->role == ui::AX_ROLE_COLUMN) {
556 dst->AddIntAttribute(ui::AX_ATTR_TABLE_COLUMN_INDEX, src.columnIndex());
557 WebAXObject header = src.columnHeader();
558 if (!header.isDetached())
559 dst->AddIntAttribute(ui::AX_ATTR_TABLE_COLUMN_HEADER_ID, header.axID());
562 if (dst->role == ui::AX_ROLE_CELL ||
563 dst->role == ui::AX_ROLE_ROW_HEADER ||
564 dst->role == ui::AX_ROLE_COLUMN_HEADER) {
565 dst->AddIntAttribute(ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX,
566 src.cellColumnIndex());
567 dst->AddIntAttribute(ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN,
568 src.cellColumnSpan());
569 dst->AddIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX, src.cellRowIndex());
570 dst->AddIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_SPAN, src.cellRowSpan());
573 if ((dst->role == ui::AX_ROLE_ROW_HEADER ||
574 dst->role == ui::AX_ROLE_COLUMN_HEADER) && src.sortDirection()) {
575 dst->AddIntAttribute(ui::AX_ATTR_SORT_DIRECTION,
576 AXSortDirectionFromBlink(src.sortDirection()));
579 dst->AddStringAttribute(ui::AX_ATTR_NAME, name);
581 // Add the ids of *indirect* children - those who are children of this node,
582 // but whose parent is *not* this node. One example is a table
583 // cell, which is a child of both a row and a column. Because the cell's
584 // parent is the row, the row adds it as a child, and the column adds it
585 // as an indirect child.
586 int child_count = src.childCount();
587 for (int i = 0; i < child_count; ++i) {
588 WebAXObject child = src.childAt(i);
589 std::vector<int32> indirect_child_ids;
590 if (!is_iframe && !child.isDetached() && !IsParentUnignoredOf(src, child))
591 indirect_child_ids.push_back(child.axID());
592 if (indirect_child_ids.size() > 0) {
593 dst->AddIntListAttribute(
594 ui::AX_ATTR_INDIRECT_CHILD_IDS, indirect_child_ids);
598 WebVector<WebAXObject> controls;
599 if (src.ariaControls(controls))
600 AddIntListAttributeFromWebObjects(ui::AX_ATTR_CONTROLS_IDS, controls, dst);
602 WebVector<WebAXObject> describedby;
603 if (src.deprecatedAriaDescribedby(describedby)) {
604 AddIntListAttributeFromWebObjects(
605 ui::AX_ATTR_DESCRIBEDBY_IDS, describedby, dst);
608 WebVector<WebAXObject> flowTo;
609 if (src.ariaFlowTo(flowTo))
610 AddIntListAttributeFromWebObjects(ui::AX_ATTR_FLOWTO_IDS, flowTo, dst);
612 WebVector<WebAXObject> labelledby;
613 if (src.deprecatedAriaLabelledby(labelledby)) {
614 AddIntListAttributeFromWebObjects(
615 ui::AX_ATTR_LABELLEDBY_IDS, labelledby, dst);
618 WebVector<WebAXObject> owns;
619 if (src.ariaOwns(owns))
620 AddIntListAttributeFromWebObjects(ui::AX_ATTR_OWNS_IDS, owns, dst);
623 if (src.isScrollableContainer()) {
624 const gfx::Point& scrollOffset = src.scrollOffset();
625 dst->AddIntAttribute(ui::AX_ATTR_SCROLL_X, scrollOffset.x());
626 dst->AddIntAttribute(ui::AX_ATTR_SCROLL_Y, scrollOffset.y());
628 const gfx::Point& minScrollOffset = src.minimumScrollOffset();
629 dst->AddIntAttribute(ui::AX_ATTR_SCROLL_X_MIN, minScrollOffset.x());
630 dst->AddIntAttribute(ui::AX_ATTR_SCROLL_Y_MIN, minScrollOffset.y());
632 const gfx::Point& maxScrollOffset = src.maximumScrollOffset();
633 dst->AddIntAttribute(ui::AX_ATTR_SCROLL_X_MAX, maxScrollOffset.x());
634 dst->AddIntAttribute(ui::AX_ATTR_SCROLL_Y_MAX, maxScrollOffset.y());
638 blink::WebDocument BlinkAXTreeSource::GetMainDocument() const {
639 if (render_frame_ && render_frame_->GetWebFrame())
640 return render_frame_->GetWebFrame()->document();
641 return WebDocument();
644 } // namespace content