Uses isEditable and isRichlyEditable to determine which attributes to expose on Mac...
[chromium-blink-merge.git] / content / renderer / accessibility / blink_ax_tree_source.cc
blobd765d8e26d4360e2bd61848d2143f9ad61746983
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/common/accessibility_messages.h"
13 #include "content/renderer/accessibility/blink_ax_enum_conversion.h"
14 #include "content/renderer/accessibility/renderer_accessibility.h"
15 #include "content/renderer/browser_plugin/browser_plugin.h"
16 #include "content/renderer/render_frame_impl.h"
17 #include "content/renderer/render_frame_proxy.h"
18 #include "content/renderer/render_view_impl.h"
19 #include "third_party/WebKit/public/platform/WebRect.h"
20 #include "third_party/WebKit/public/platform/WebSize.h"
21 #include "third_party/WebKit/public/platform/WebString.h"
22 #include "third_party/WebKit/public/platform/WebVector.h"
23 #include "third_party/WebKit/public/web/WebAXEnums.h"
24 #include "third_party/WebKit/public/web/WebAXObject.h"
25 #include "third_party/WebKit/public/web/WebDocument.h"
26 #include "third_party/WebKit/public/web/WebDocumentType.h"
27 #include "third_party/WebKit/public/web/WebElement.h"
28 #include "third_party/WebKit/public/web/WebFormControlElement.h"
29 #include "third_party/WebKit/public/web/WebFrame.h"
30 #include "third_party/WebKit/public/web/WebLocalFrame.h"
31 #include "third_party/WebKit/public/web/WebNode.h"
32 #include "third_party/WebKit/public/web/WebPlugin.h"
33 #include "third_party/WebKit/public/web/WebPluginContainer.h"
34 #include "third_party/WebKit/public/web/WebView.h"
36 using base::ASCIIToUTF16;
37 using base::UTF16ToUTF8;
38 using blink::WebAXObject;
39 using blink::WebDocument;
40 using blink::WebDocumentType;
41 using blink::WebElement;
42 using blink::WebFrame;
43 using blink::WebLocalFrame;
44 using blink::WebNode;
45 using blink::WebPlugin;
46 using blink::WebPluginContainer;
47 using blink::WebVector;
48 using blink::WebView;
50 namespace content {
52 namespace {
54 // Returns true if |ancestor| is the first unignored parent of |child|,
55 // which means that when walking up the parent chain from |child|,
56 // |ancestor| is the *first* ancestor that isn't marked as
57 // accessibilityIsIgnored().
58 bool IsParentUnignoredOf(WebAXObject ancestor,
59 WebAXObject child) {
60 WebAXObject parent = child.parentObject();
61 while (!parent.isDetached() && parent.accessibilityIsIgnored())
62 parent = parent.parentObject();
63 return parent.equals(ancestor);
66 std::string GetEquivalentAriaRoleString(const ui::AXRole role) {
67 switch (role) {
68 case ui::AX_ROLE_ARTICLE:
69 return "article";
70 case ui::AX_ROLE_BANNER:
71 return "banner";
72 case ui::AX_ROLE_BUTTON:
73 return "button";
74 case ui::AX_ROLE_COMPLEMENTARY:
75 return "complementary";
76 case ui::AX_ROLE_FIGURE:
77 return "figure";
78 case ui::AX_ROLE_FOOTER:
79 return "contentinfo";
80 case ui::AX_ROLE_HEADING:
81 return "heading";
82 case ui::AX_ROLE_IMAGE:
83 return "img";
84 case ui::AX_ROLE_MAIN:
85 return "main";
86 case ui::AX_ROLE_NAVIGATION:
87 return "navigation";
88 case ui::AX_ROLE_RADIO_BUTTON:
89 return "radio";
90 case ui::AX_ROLE_REGION:
91 return "region";
92 case ui::AX_ROLE_SLIDER:
93 return "slider";
94 default:
95 break;
98 return std::string();
101 void AddIntListAttributeFromWebObjects(ui::AXIntListAttribute attr,
102 WebVector<WebAXObject> objects,
103 AXContentNodeData* dst) {
104 std::vector<int32> ids;
105 for(size_t i = 0; i < objects.size(); i++)
106 ids.push_back(objects[i].axID());
107 if (ids.size() > 0)
108 dst->AddIntListAttribute(attr, ids);
111 } // Anonymous namespace
113 BlinkAXTreeSource::BlinkAXTreeSource(RenderFrameImpl* render_frame)
114 : render_frame_(render_frame),
115 accessibility_focus_id_(-1) {
118 BlinkAXTreeSource::~BlinkAXTreeSource() {
121 void BlinkAXTreeSource::SetRoot(blink::WebAXObject root) {
122 root_ = root;
125 bool BlinkAXTreeSource::IsInTree(blink::WebAXObject node) const {
126 const blink::WebAXObject& root = GetRoot();
127 while (IsValid(node)) {
128 if (node.equals(root))
129 return true;
130 node = GetParent(node);
132 return false;
135 blink::WebAXObject BlinkAXTreeSource::GetRoot() const {
136 if (!root_.isNull())
137 return root_;
138 return GetMainDocument().accessibilityObject();
141 blink::WebAXObject BlinkAXTreeSource::GetFromId(int32 id) const {
142 return GetMainDocument().accessibilityObjectFromID(id);
145 int32 BlinkAXTreeSource::GetId(blink::WebAXObject node) const {
146 return node.axID();
149 void BlinkAXTreeSource::GetChildren(
150 blink::WebAXObject parent,
151 std::vector<blink::WebAXObject>* out_children) const {
152 if (parent.role() == blink::WebAXRoleStaticText) {
153 blink::WebAXObject ancestor = parent;
154 while (!ancestor.isDetached()) {
155 if (ancestor.axID() == accessibility_focus_id_) {
156 parent.loadInlineTextBoxes();
157 break;
159 ancestor = ancestor.parentObject();
163 bool is_iframe = false;
164 WebNode node = parent.node();
165 if (!node.isNull() && node.isElementNode()) {
166 WebElement element = node.to<WebElement>();
167 is_iframe = (element.tagName() == ASCIIToUTF16("IFRAME"));
170 for (unsigned i = 0; i < parent.childCount(); i++) {
171 blink::WebAXObject child = parent.childAt(i);
173 // The child may be invalid due to issues in blink accessibility code.
174 if (child.isDetached())
175 continue;
177 // Skip children whose parent isn't |parent|.
178 // As an exception, include children of an iframe element.
179 if (!is_iframe && !IsParentUnignoredOf(parent, child))
180 continue;
182 out_children->push_back(child);
186 blink::WebAXObject BlinkAXTreeSource::GetParent(
187 blink::WebAXObject node) const {
188 // Blink returns ignored objects when walking up the parent chain,
189 // we have to skip those here. Also, stop when we get to the root
190 // element.
191 blink::WebAXObject root = GetRoot();
192 do {
193 if (node.equals(root))
194 return blink::WebAXObject();
195 node = node.parentObject();
196 } while (!node.isDetached() && node.accessibilityIsIgnored());
198 return node;
201 bool BlinkAXTreeSource::IsValid(blink::WebAXObject node) const {
202 return !node.isDetached(); // This also checks if it's null.
205 bool BlinkAXTreeSource::IsEqual(blink::WebAXObject node1,
206 blink::WebAXObject node2) const {
207 return node1.equals(node2);
210 blink::WebAXObject BlinkAXTreeSource::GetNull() const {
211 return blink::WebAXObject();
214 void BlinkAXTreeSource::SerializeNode(blink::WebAXObject src,
215 AXContentNodeData* dst) const {
216 dst->role = AXRoleFromBlink(src.role());
217 dst->state = AXStateFromBlink(src);
218 dst->location = src.boundingBoxRect();
219 dst->id = src.axID();
220 std::string name = UTF16ToUTF8(base::StringPiece16(src.deprecatedTitle()));
222 std::string value;
223 if (src.valueDescription().length()) {
224 dst->AddStringAttribute(ui::AX_ATTR_VALUE,
225 UTF16ToUTF8(base::StringPiece16(
226 src.valueDescription())));
227 } else {
228 dst->AddStringAttribute(
229 ui::AX_ATTR_VALUE,
230 UTF16ToUTF8(base::StringPiece16(src.stringValue())));
233 if (dst->role == ui::AX_ROLE_COLOR_WELL)
234 dst->AddIntAttribute(ui::AX_ATTR_COLOR_VALUE, src.colorValue());
237 // Text attributes.
238 if (src.backgroundColor())
239 dst->AddIntAttribute(ui::AX_ATTR_BACKGROUND_COLOR, src.backgroundColor());
241 if (src.color())
242 dst->AddIntAttribute(ui::AX_ATTR_COLOR, src.color());
244 // Font size is in pixels.
245 if (src.fontSize())
246 dst->AddFloatAttribute(ui::AX_ATTR_FONT_SIZE, src.fontSize());
248 if (src.invalidState()) {
249 dst->AddIntAttribute(ui::AX_ATTR_INVALID_STATE,
250 AXInvalidStateFromBlink(src.invalidState()));
252 if (src.invalidState() == blink::WebAXInvalidStateOther) {
253 dst->AddStringAttribute(
254 ui::AX_ATTR_ARIA_INVALID_VALUE,
255 UTF16ToUTF8(base::StringPiece16(src.ariaInvalidValue())));
258 if (src.textDirection()) {
259 dst->AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
260 AXTextDirectionFromBlink(src.textDirection()));
263 if (src.textStyle()) {
264 dst->AddIntAttribute(ui::AX_ATTR_TEXT_STYLE,
265 AXTextStyleFromBlink(src.textStyle()));
269 if (dst->role == ui::AX_ROLE_INLINE_TEXT_BOX) {
270 WebVector<int> src_character_offsets;
271 src.characterOffsets(src_character_offsets);
272 std::vector<int32> character_offsets;
273 character_offsets.reserve(src_character_offsets.size());
274 for (size_t i = 0; i < src_character_offsets.size(); ++i)
275 character_offsets.push_back(src_character_offsets[i]);
276 dst->AddIntListAttribute(ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets);
278 WebVector<int> src_word_starts;
279 WebVector<int> src_word_ends;
280 src.wordBoundaries(src_word_starts, src_word_ends);
281 std::vector<int32> word_starts;
282 std::vector<int32> word_ends;
283 word_starts.reserve(src_word_starts.size());
284 word_ends.reserve(src_word_starts.size());
285 for (size_t i = 0; i < src_word_starts.size(); ++i) {
286 word_starts.push_back(src_word_starts[i]);
287 word_ends.push_back(src_word_ends[i]);
289 dst->AddIntListAttribute(ui::AX_ATTR_WORD_STARTS, word_starts);
290 dst->AddIntListAttribute(ui::AX_ATTR_WORD_ENDS, word_ends);
293 if (src.accessKey().length()) {
294 dst->AddStringAttribute(ui::AX_ATTR_ACCESS_KEY,
295 UTF16ToUTF8(base::StringPiece16(src.accessKey())));
298 if (src.actionVerb().length())
299 dst->AddStringAttribute(
300 ui::AX_ATTR_ACTION,
301 UTF16ToUTF8(base::StringPiece16(src.actionVerb())));
302 if (src.ariaAutoComplete().length())
303 dst->AddStringAttribute(
304 ui::AX_ATTR_AUTO_COMPLETE,
305 UTF16ToUTF8(base::StringPiece16(src.ariaAutoComplete())));
306 if (src.isAriaReadOnly())
307 dst->AddBoolAttribute(ui::AX_ATTR_ARIA_READONLY, true);
308 if (src.isButtonStateMixed())
309 dst->AddBoolAttribute(ui::AX_ATTR_BUTTON_MIXED, true);
310 if (src.canSetValueAttribute())
311 dst->AddBoolAttribute(ui::AX_ATTR_CAN_SET_VALUE, true);
312 if (src.deprecatedAccessibilityDescription().length()) {
313 dst->AddStringAttribute(
314 ui::AX_ATTR_DESCRIPTION,
315 UTF16ToUTF8(base::StringPiece16(
316 src.deprecatedAccessibilityDescription())));
318 if (src.hasComputedStyle()) {
319 dst->AddStringAttribute(
320 ui::AX_ATTR_DISPLAY,
321 UTF16ToUTF8(base::StringPiece16(src.computedStyleDisplay())));
323 if (src.deprecatedHelpText().length())
324 dst->AddStringAttribute(
325 ui::AX_ATTR_HELP,
326 UTF16ToUTF8(base::StringPiece16((src.deprecatedHelpText()))));
327 if (src.deprecatedPlaceholder().length()) {
328 dst->AddStringAttribute(
329 ui::AX_ATTR_PLACEHOLDER,
330 UTF16ToUTF8(base::StringPiece16(src.deprecatedPlaceholder())));
332 if (src.keyboardShortcut().length()) {
333 dst->AddStringAttribute(
334 ui::AX_ATTR_SHORTCUT,
335 UTF16ToUTF8(base::StringPiece16(src.keyboardShortcut())));
337 if (!src.deprecatedTitleUIElement().isDetached()) {
338 dst->AddIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT,
339 src.deprecatedTitleUIElement().axID());
341 if (!src.ariaActiveDescendant().isDetached()) {
342 dst->AddIntAttribute(ui::AX_ATTR_ACTIVEDESCENDANT_ID,
343 src.ariaActiveDescendant().axID());
346 if (!src.url().isEmpty())
347 dst->AddStringAttribute(ui::AX_ATTR_URL, src.url().spec());
349 if (dst->role == ui::AX_ROLE_HEADING)
350 dst->AddIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL, src.headingLevel());
351 else if ((dst->role == ui::AX_ROLE_TREE_ITEM ||
352 dst->role == ui::AX_ROLE_ROW) &&
353 src.hierarchicalLevel() > 0) {
354 dst->AddIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL,
355 src.hierarchicalLevel());
358 if (src.setSize())
359 dst->AddIntAttribute(ui::AX_ATTR_SET_SIZE, src.setSize());
361 if (src.posInSet())
362 dst->AddIntAttribute(ui::AX_ATTR_POS_IN_SET, src.posInSet());
364 // Treat the active list box item as focused.
365 if (dst->role == ui::AX_ROLE_LIST_BOX_OPTION &&
366 src.isSelectedOptionActive()) {
367 dst->state |= (1 << ui::AX_STATE_FOCUSED);
370 if (src.canvasHasFallbackContent())
371 dst->AddBoolAttribute(ui::AX_ATTR_CANVAS_HAS_FALLBACK, true);
373 WebNode node = src.node();
374 bool is_iframe = false;
376 if (!node.isNull() && node.isElementNode()) {
377 WebElement element = node.to<WebElement>();
378 is_iframe = (element.tagName() == ASCIIToUTF16("IFRAME"));
380 // TODO(ctguil): The tagName in WebKit is lower cased but
381 // HTMLElement::nodeName calls localNameUpper. Consider adding
382 // a WebElement method that returns the original lower cased tagName.
383 dst->AddStringAttribute(
384 ui::AX_ATTR_HTML_TAG,
385 base::ToLowerASCII(UTF16ToUTF8(
386 base::StringPiece16(element.tagName()))));
387 for (unsigned i = 0; i < element.attributeCount(); ++i) {
388 std::string name = base::ToLowerASCII(UTF16ToUTF8(
389 base::StringPiece16(element.attributeLocalName(i))));
390 std::string value =
391 UTF16ToUTF8(base::StringPiece16(element.attributeValue(i)));
392 dst->html_attributes.push_back(std::make_pair(name, value));
395 if (src.isEditable()) {
396 dst->AddIntAttribute(ui::AX_ATTR_TEXT_SEL_START, src.selectionStart());
397 dst->AddIntAttribute(ui::AX_ATTR_TEXT_SEL_END, src.selectionEnd());
399 WebVector<int> src_line_breaks;
400 src.lineBreaks(src_line_breaks);
401 if (src_line_breaks.size() > 0) {
402 std::vector<int32> line_breaks;
403 line_breaks.reserve(src_line_breaks.size());
404 for (size_t i = 0; i < src_line_breaks.size(); ++i)
405 line_breaks.push_back(src_line_breaks[i]);
406 dst->AddIntListAttribute(ui::AX_ATTR_LINE_BREAKS, line_breaks);
410 // ARIA role.
411 if (element.hasAttribute("role")) {
412 dst->AddStringAttribute(
413 ui::AX_ATTR_ROLE,
414 UTF16ToUTF8(base::StringPiece16(element.getAttribute("role"))));
415 } else {
416 std::string role = GetEquivalentAriaRoleString(dst->role);
417 if (!role.empty())
418 dst->AddStringAttribute(ui::AX_ATTR_ROLE, role);
419 else if (dst->role == ui::AX_ROLE_TIME)
420 dst->AddStringAttribute(ui::AX_ATTR_ROLE, "time");
423 // Browser plugin (used in a <webview>).
424 BrowserPlugin* browser_plugin = BrowserPlugin::GetFromNode(element);
425 if (browser_plugin) {
426 dst->AddContentIntAttribute(
427 AX_CONTENT_ATTR_CHILD_BROWSER_PLUGIN_INSTANCE_ID,
428 browser_plugin->browser_plugin_instance_id());
431 // Out-of-process iframe.
432 if (is_iframe) {
433 WebFrame* frame = WebFrame::fromFrameOwnerElement(element);
435 if (frame && frame->isWebRemoteFrame()) {
436 RenderFrameProxy* render_frame_proxy =
437 RenderFrameProxy::FromWebFrame(frame);
438 DCHECK(render_frame_proxy);
439 dst->AddContentIntAttribute(
440 AX_CONTENT_ATTR_CHILD_ROUTING_ID,
441 render_frame_proxy->routing_id());
446 if (src.isInLiveRegion()) {
447 dst->AddBoolAttribute(ui::AX_ATTR_LIVE_ATOMIC, src.liveRegionAtomic());
448 dst->AddBoolAttribute(ui::AX_ATTR_LIVE_BUSY, src.liveRegionBusy());
449 if (src.liveRegionBusy())
450 dst->state |= (1 << ui::AX_STATE_BUSY);
451 if (!src.liveRegionStatus().isEmpty()) {
452 dst->AddStringAttribute(
453 ui::AX_ATTR_LIVE_STATUS,
454 UTF16ToUTF8(base::StringPiece16(src.liveRegionStatus())));
456 dst->AddStringAttribute(
457 ui::AX_ATTR_LIVE_RELEVANT,
458 UTF16ToUTF8(base::StringPiece16(src.liveRegionRelevant())));
459 dst->AddBoolAttribute(ui::AX_ATTR_CONTAINER_LIVE_ATOMIC,
460 src.containerLiveRegionAtomic());
461 dst->AddBoolAttribute(ui::AX_ATTR_CONTAINER_LIVE_BUSY,
462 src.containerLiveRegionBusy());
463 dst->AddStringAttribute(
464 ui::AX_ATTR_CONTAINER_LIVE_STATUS,
465 UTF16ToUTF8(base::StringPiece16(src.containerLiveRegionStatus())));
466 dst->AddStringAttribute(
467 ui::AX_ATTR_CONTAINER_LIVE_RELEVANT,
468 UTF16ToUTF8(base::StringPiece16(src.containerLiveRegionRelevant())));
471 if (dst->role == ui::AX_ROLE_PROGRESS_INDICATOR ||
472 dst->role == ui::AX_ROLE_METER ||
473 dst->role == ui::AX_ROLE_SCROLL_BAR ||
474 dst->role == ui::AX_ROLE_SLIDER ||
475 dst->role == ui::AX_ROLE_SPIN_BUTTON) {
476 dst->AddFloatAttribute(ui::AX_ATTR_VALUE_FOR_RANGE, src.valueForRange());
477 dst->AddFloatAttribute(ui::AX_ATTR_MAX_VALUE_FOR_RANGE,
478 src.maxValueForRange());
479 dst->AddFloatAttribute(ui::AX_ATTR_MIN_VALUE_FOR_RANGE,
480 src.minValueForRange());
483 if (dst->role == ui::AX_ROLE_WEB_AREA) {
484 dst->AddStringAttribute(ui::AX_ATTR_HTML_TAG, "#document");
485 const WebDocument& document = src.document();
486 if (name.empty())
487 name = UTF16ToUTF8(base::StringPiece16(document.title()));
488 dst->AddStringAttribute(
489 ui::AX_ATTR_DOC_TITLE,
490 UTF16ToUTF8(base::StringPiece16(document.title())));
491 dst->AddStringAttribute(ui::AX_ATTR_DOC_URL, document.url().spec());
492 dst->AddStringAttribute(
493 ui::AX_ATTR_DOC_MIMETYPE,
494 document.isXHTMLDocument() ? "text/xhtml" : "text/html");
495 dst->AddBoolAttribute(ui::AX_ATTR_DOC_LOADED, src.isLoaded());
496 dst->AddFloatAttribute(ui::AX_ATTR_DOC_LOADING_PROGRESS,
497 src.estimatedLoadingProgress());
499 const WebDocumentType& doctype = document.doctype();
500 if (!doctype.isNull()) {
501 dst->AddStringAttribute(
502 ui::AX_ATTR_DOC_DOCTYPE,
503 UTF16ToUTF8(base::StringPiece16(doctype.name())));
506 WebAXObject anchor_object, focus_object;
507 int anchor_offset, focus_offset;
508 src.selection(anchor_object, anchor_offset, focus_object, focus_offset);
509 if (!anchor_object.isNull() && !focus_object.isNull() &&
510 anchor_offset >= 0 && focus_offset >= 0) {
511 int32 anchor_id = anchor_object.axID();
512 int32 focus_id = focus_object.axID();
513 dst->AddIntAttribute(ui::AX_ATTR_ANCHOR_OBJECT_ID, anchor_id);
514 dst->AddIntAttribute(ui::AX_ATTR_ANCHOR_OFFSET, anchor_offset);
515 dst->AddIntAttribute(ui::AX_ATTR_FOCUS_OBJECT_ID, focus_id);
516 dst->AddIntAttribute(ui::AX_ATTR_FOCUS_OFFSET, focus_offset);
519 // Get the tree ID for this frame and possibly the parent frame.
520 WebLocalFrame* web_frame = document.frame();
521 if (web_frame) {
522 RenderFrame* render_frame = RenderFrame::FromWebFrame(web_frame);
523 dst->AddContentIntAttribute(
524 AX_CONTENT_ATTR_ROUTING_ID,
525 render_frame->GetRoutingID());
527 // Get the tree ID for the parent frame, if it's remote.
528 // (If it's local, it's already part of this same tree.)
529 blink::WebFrame* parent_web_frame = web_frame->parent();
530 if (parent_web_frame && parent_web_frame->isWebRemoteFrame()) {
531 RenderFrameProxy* parent_render_frame_proxy =
532 RenderFrameProxy::FromWebFrame(parent_web_frame);
533 dst->AddContentIntAttribute(
534 AX_CONTENT_ATTR_PARENT_ROUTING_ID,
535 parent_render_frame_proxy->routing_id());
540 if (dst->role == ui::AX_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<int32> unique_cell_id_set;
545 std::vector<int32> cell_ids;
546 std::vector<int32> unique_cell_ids;
547 dst->AddIntAttribute(ui::AX_ATTR_TABLE_COLUMN_COUNT, column_count);
548 dst->AddIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, row_count);
549 WebAXObject header = src.headerContainerObject();
550 if (!header.isDetached())
551 dst->AddIntAttribute(ui::AX_ATTR_TABLE_HEADER_ID, header.axID());
552 for (int i = 0; i < column_count * row_count; ++i) {
553 WebAXObject cell = src.cellForColumnAndRow(
554 i % column_count, i / column_count);
555 int cell_id = -1;
556 if (!cell.isDetached()) {
557 cell_id = cell.axID();
558 if (unique_cell_id_set.find(cell_id) == unique_cell_id_set.end()) {
559 unique_cell_id_set.insert(cell_id);
560 unique_cell_ids.push_back(cell_id);
563 cell_ids.push_back(cell_id);
565 dst->AddIntListAttribute(ui::AX_ATTR_CELL_IDS, cell_ids);
566 dst->AddIntListAttribute(ui::AX_ATTR_UNIQUE_CELL_IDS, unique_cell_ids);
570 if (dst->role == ui::AX_ROLE_ROW) {
571 dst->AddIntAttribute(ui::AX_ATTR_TABLE_ROW_INDEX, src.rowIndex());
572 WebAXObject header = src.rowHeader();
573 if (!header.isDetached())
574 dst->AddIntAttribute(ui::AX_ATTR_TABLE_ROW_HEADER_ID, header.axID());
577 if (dst->role == ui::AX_ROLE_COLUMN) {
578 dst->AddIntAttribute(ui::AX_ATTR_TABLE_COLUMN_INDEX, src.columnIndex());
579 WebAXObject header = src.columnHeader();
580 if (!header.isDetached())
581 dst->AddIntAttribute(ui::AX_ATTR_TABLE_COLUMN_HEADER_ID, header.axID());
584 if (dst->role == ui::AX_ROLE_CELL ||
585 dst->role == ui::AX_ROLE_ROW_HEADER ||
586 dst->role == ui::AX_ROLE_COLUMN_HEADER) {
587 dst->AddIntAttribute(ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX,
588 src.cellColumnIndex());
589 dst->AddIntAttribute(ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN,
590 src.cellColumnSpan());
591 dst->AddIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX, src.cellRowIndex());
592 dst->AddIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_SPAN, src.cellRowSpan());
595 if ((dst->role == ui::AX_ROLE_ROW_HEADER ||
596 dst->role == ui::AX_ROLE_COLUMN_HEADER) && src.sortDirection()) {
597 dst->AddIntAttribute(ui::AX_ATTR_SORT_DIRECTION,
598 AXSortDirectionFromBlink(src.sortDirection()));
601 dst->AddStringAttribute(ui::AX_ATTR_NAME, name);
603 // Add the ids of *indirect* children - those who are children of this node,
604 // but whose parent is *not* this node. One example is a table
605 // cell, which is a child of both a row and a column. Because the cell's
606 // parent is the row, the row adds it as a child, and the column adds it
607 // as an indirect child.
608 int child_count = src.childCount();
609 for (int i = 0; i < child_count; ++i) {
610 WebAXObject child = src.childAt(i);
611 std::vector<int32> indirect_child_ids;
612 if (!is_iframe && !child.isDetached() && !IsParentUnignoredOf(src, child))
613 indirect_child_ids.push_back(child.axID());
614 if (indirect_child_ids.size() > 0) {
615 dst->AddIntListAttribute(
616 ui::AX_ATTR_INDIRECT_CHILD_IDS, indirect_child_ids);
620 WebVector<WebAXObject> controls;
621 if (src.ariaControls(controls))
622 AddIntListAttributeFromWebObjects(ui::AX_ATTR_CONTROLS_IDS, controls, dst);
624 WebVector<WebAXObject> describedby;
625 if (src.deprecatedAriaDescribedby(describedby)) {
626 AddIntListAttributeFromWebObjects(
627 ui::AX_ATTR_DESCRIBEDBY_IDS, describedby, dst);
630 WebVector<WebAXObject> flowTo;
631 if (src.ariaFlowTo(flowTo))
632 AddIntListAttributeFromWebObjects(ui::AX_ATTR_FLOWTO_IDS, flowTo, dst);
634 WebVector<WebAXObject> labelledby;
635 if (src.deprecatedAriaLabelledby(labelledby)) {
636 AddIntListAttributeFromWebObjects(
637 ui::AX_ATTR_LABELLEDBY_IDS, labelledby, dst);
640 WebVector<WebAXObject> owns;
641 if (src.ariaOwns(owns))
642 AddIntListAttributeFromWebObjects(ui::AX_ATTR_OWNS_IDS, owns, dst);
645 if (src.isScrollableContainer()) {
646 const gfx::Point& scrollOffset = src.scrollOffset();
647 dst->AddIntAttribute(ui::AX_ATTR_SCROLL_X, scrollOffset.x());
648 dst->AddIntAttribute(ui::AX_ATTR_SCROLL_Y, scrollOffset.y());
650 const gfx::Point& minScrollOffset = src.minimumScrollOffset();
651 dst->AddIntAttribute(ui::AX_ATTR_SCROLL_X_MIN, minScrollOffset.x());
652 dst->AddIntAttribute(ui::AX_ATTR_SCROLL_Y_MIN, minScrollOffset.y());
654 const gfx::Point& maxScrollOffset = src.maximumScrollOffset();
655 dst->AddIntAttribute(ui::AX_ATTR_SCROLL_X_MAX, maxScrollOffset.x());
656 dst->AddIntAttribute(ui::AX_ATTR_SCROLL_Y_MAX, maxScrollOffset.y());
660 blink::WebDocument BlinkAXTreeSource::GetMainDocument() const {
661 if (render_frame_ && render_frame_->GetWebFrame())
662 return render_frame_->GetWebFrame()->document();
663 return WebDocument();
666 } // namespace content