Roll src/third_party/skia 2440fcd:4de8c3a
[chromium-blink-merge.git] / content / renderer / accessibility / blink_ax_tree_source.cc
blob765a5747cb629e050b48bd761d2c8790817a75c1
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 is_iframe = node.to<WebElement>().hasHTMLTagName("iframe");
168 for (unsigned i = 0; i < parent.childCount(); i++) {
169 blink::WebAXObject child = parent.childAt(i);
171 // The child may be invalid due to issues in blink accessibility code.
172 if (child.isDetached())
173 continue;
175 // Skip children whose parent isn't |parent|.
176 // As an exception, include children of an iframe element.
177 if (!is_iframe && !IsParentUnignoredOf(parent, child))
178 continue;
180 out_children->push_back(child);
184 blink::WebAXObject BlinkAXTreeSource::GetParent(
185 blink::WebAXObject node) const {
186 // Blink returns ignored objects when walking up the parent chain,
187 // we have to skip those here. Also, stop when we get to the root
188 // element.
189 blink::WebAXObject root = GetRoot();
190 do {
191 if (node.equals(root))
192 return blink::WebAXObject();
193 node = node.parentObject();
194 } while (!node.isDetached() && node.accessibilityIsIgnored());
196 return node;
199 bool BlinkAXTreeSource::IsValid(blink::WebAXObject node) const {
200 return !node.isDetached(); // This also checks if it's null.
203 bool BlinkAXTreeSource::IsEqual(blink::WebAXObject node1,
204 blink::WebAXObject node2) const {
205 return node1.equals(node2);
208 blink::WebAXObject BlinkAXTreeSource::GetNull() const {
209 return blink::WebAXObject();
212 void BlinkAXTreeSource::SerializeNode(blink::WebAXObject src,
213 AXContentNodeData* dst) const {
214 dst->role = AXRoleFromBlink(src.role());
215 dst->state = AXStateFromBlink(src);
216 dst->location = src.boundingBoxRect();
217 dst->id = src.axID();
218 std::string name = UTF16ToUTF8(base::StringPiece16(src.deprecatedTitle()));
220 std::string value;
221 if (src.valueDescription().length()) {
222 dst->AddStringAttribute(ui::AX_ATTR_VALUE,
223 UTF16ToUTF8(base::StringPiece16(
224 src.valueDescription())));
225 } else {
226 dst->AddStringAttribute(
227 ui::AX_ATTR_VALUE,
228 UTF16ToUTF8(base::StringPiece16(src.stringValue())));
231 if (dst->role == ui::AX_ROLE_COLOR_WELL)
232 dst->AddIntAttribute(ui::AX_ATTR_COLOR_VALUE, src.colorValue());
235 // Text attributes.
236 if (src.backgroundColor())
237 dst->AddIntAttribute(ui::AX_ATTR_BACKGROUND_COLOR, src.backgroundColor());
239 if (src.color())
240 dst->AddIntAttribute(ui::AX_ATTR_COLOR, src.color());
242 // Font size is in pixels.
243 if (src.fontSize())
244 dst->AddFloatAttribute(ui::AX_ATTR_FONT_SIZE, src.fontSize());
246 if (src.invalidState()) {
247 dst->AddIntAttribute(ui::AX_ATTR_INVALID_STATE,
248 AXInvalidStateFromBlink(src.invalidState()));
250 if (src.invalidState() == blink::WebAXInvalidStateOther) {
251 dst->AddStringAttribute(
252 ui::AX_ATTR_ARIA_INVALID_VALUE,
253 UTF16ToUTF8(base::StringPiece16(src.ariaInvalidValue())));
256 if (src.textDirection()) {
257 dst->AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION,
258 AXTextDirectionFromBlink(src.textDirection()));
261 if (src.textStyle()) {
262 dst->AddIntAttribute(ui::AX_ATTR_TEXT_STYLE,
263 AXTextStyleFromBlink(src.textStyle()));
267 if (dst->role == ui::AX_ROLE_INLINE_TEXT_BOX) {
268 WebVector<int> src_character_offsets;
269 src.characterOffsets(src_character_offsets);
270 std::vector<int32> character_offsets;
271 character_offsets.reserve(src_character_offsets.size());
272 for (size_t i = 0; i < src_character_offsets.size(); ++i)
273 character_offsets.push_back(src_character_offsets[i]);
274 dst->AddIntListAttribute(ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets);
276 WebVector<int> src_word_starts;
277 WebVector<int> src_word_ends;
278 src.wordBoundaries(src_word_starts, src_word_ends);
279 std::vector<int32> word_starts;
280 std::vector<int32> word_ends;
281 word_starts.reserve(src_word_starts.size());
282 word_ends.reserve(src_word_starts.size());
283 for (size_t i = 0; i < src_word_starts.size(); ++i) {
284 word_starts.push_back(src_word_starts[i]);
285 word_ends.push_back(src_word_ends[i]);
287 dst->AddIntListAttribute(ui::AX_ATTR_WORD_STARTS, word_starts);
288 dst->AddIntListAttribute(ui::AX_ATTR_WORD_ENDS, word_ends);
291 if (src.accessKey().length()) {
292 dst->AddStringAttribute(ui::AX_ATTR_ACCESS_KEY,
293 UTF16ToUTF8(base::StringPiece16(src.accessKey())));
296 if (src.actionVerb().length())
297 dst->AddStringAttribute(
298 ui::AX_ATTR_ACTION,
299 UTF16ToUTF8(base::StringPiece16(src.actionVerb())));
300 if (src.ariaAutoComplete().length())
301 dst->AddStringAttribute(
302 ui::AX_ATTR_AUTO_COMPLETE,
303 UTF16ToUTF8(base::StringPiece16(src.ariaAutoComplete())));
304 if (src.isAriaReadOnly())
305 dst->AddBoolAttribute(ui::AX_ATTR_ARIA_READONLY, true);
306 if (src.isButtonStateMixed())
307 dst->AddBoolAttribute(ui::AX_ATTR_BUTTON_MIXED, true);
308 if (src.canSetValueAttribute())
309 dst->AddBoolAttribute(ui::AX_ATTR_CAN_SET_VALUE, true);
310 if (src.deprecatedAccessibilityDescription().length()) {
311 dst->AddStringAttribute(
312 ui::AX_ATTR_DESCRIPTION,
313 UTF16ToUTF8(base::StringPiece16(
314 src.deprecatedAccessibilityDescription())));
316 if (src.hasComputedStyle()) {
317 dst->AddStringAttribute(
318 ui::AX_ATTR_DISPLAY,
319 UTF16ToUTF8(base::StringPiece16(src.computedStyleDisplay())));
321 if (src.deprecatedHelpText().length())
322 dst->AddStringAttribute(
323 ui::AX_ATTR_HELP,
324 UTF16ToUTF8(base::StringPiece16((src.deprecatedHelpText()))));
325 if (src.deprecatedPlaceholder().length()) {
326 dst->AddStringAttribute(
327 ui::AX_ATTR_PLACEHOLDER,
328 UTF16ToUTF8(base::StringPiece16(src.deprecatedPlaceholder())));
330 if (src.keyboardShortcut().length()) {
331 dst->AddStringAttribute(
332 ui::AX_ATTR_SHORTCUT,
333 UTF16ToUTF8(base::StringPiece16(src.keyboardShortcut())));
335 if (!src.deprecatedTitleUIElement().isDetached()) {
336 dst->AddIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT,
337 src.deprecatedTitleUIElement().axID());
339 if (!src.ariaActiveDescendant().isDetached()) {
340 dst->AddIntAttribute(ui::AX_ATTR_ACTIVEDESCENDANT_ID,
341 src.ariaActiveDescendant().axID());
344 if (!src.url().isEmpty())
345 dst->AddStringAttribute(ui::AX_ATTR_URL, src.url().spec());
347 if (dst->role == ui::AX_ROLE_HEADING)
348 dst->AddIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL, src.headingLevel());
349 else if ((dst->role == ui::AX_ROLE_TREE_ITEM ||
350 dst->role == ui::AX_ROLE_ROW) &&
351 src.hierarchicalLevel() > 0) {
352 dst->AddIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL,
353 src.hierarchicalLevel());
356 if (src.setSize())
357 dst->AddIntAttribute(ui::AX_ATTR_SET_SIZE, src.setSize());
359 if (src.posInSet())
360 dst->AddIntAttribute(ui::AX_ATTR_POS_IN_SET, src.posInSet());
362 // Treat the active list box item as focused.
363 if (dst->role == ui::AX_ROLE_LIST_BOX_OPTION &&
364 src.isSelectedOptionActive()) {
365 dst->state |= (1 << ui::AX_STATE_FOCUSED);
368 if (src.canvasHasFallbackContent())
369 dst->AddBoolAttribute(ui::AX_ATTR_CANVAS_HAS_FALLBACK, true);
371 WebNode node = src.node();
372 bool is_iframe = false;
374 if (!node.isNull() && node.isElementNode()) {
375 WebElement element = node.to<WebElement>();
376 is_iframe = element.hasHTMLTagName("iframe");
378 // TODO(ctguil): The tagName in WebKit is lower cased but
379 // HTMLElement::nodeName calls localNameUpper. Consider adding
380 // a WebElement method that returns the original lower cased tagName.
381 dst->AddStringAttribute(
382 ui::AX_ATTR_HTML_TAG,
383 base::ToLowerASCII(UTF16ToUTF8(
384 base::StringPiece16(element.tagName()))));
385 for (unsigned i = 0; i < element.attributeCount(); ++i) {
386 std::string name = base::ToLowerASCII(UTF16ToUTF8(
387 base::StringPiece16(element.attributeLocalName(i))));
388 std::string value =
389 UTF16ToUTF8(base::StringPiece16(element.attributeValue(i)));
390 dst->html_attributes.push_back(std::make_pair(name, value));
393 if (src.isEditable()) {
394 dst->AddIntAttribute(ui::AX_ATTR_TEXT_SEL_START, src.selectionStart());
395 dst->AddIntAttribute(ui::AX_ATTR_TEXT_SEL_END, src.selectionEnd());
397 WebVector<int> src_line_breaks;
398 src.lineBreaks(src_line_breaks);
399 if (src_line_breaks.size() > 0) {
400 std::vector<int32> line_breaks;
401 line_breaks.reserve(src_line_breaks.size());
402 for (size_t i = 0; i < src_line_breaks.size(); ++i)
403 line_breaks.push_back(src_line_breaks[i]);
404 dst->AddIntListAttribute(ui::AX_ATTR_LINE_BREAKS, line_breaks);
408 // ARIA role.
409 if (element.hasAttribute("role")) {
410 dst->AddStringAttribute(
411 ui::AX_ATTR_ROLE,
412 UTF16ToUTF8(base::StringPiece16(element.getAttribute("role"))));
413 } else {
414 std::string role = GetEquivalentAriaRoleString(dst->role);
415 if (!role.empty())
416 dst->AddStringAttribute(ui::AX_ATTR_ROLE, role);
417 else if (dst->role == ui::AX_ROLE_TIME)
418 dst->AddStringAttribute(ui::AX_ATTR_ROLE, "time");
421 // Browser plugin (used in a <webview>).
422 BrowserPlugin* browser_plugin = BrowserPlugin::GetFromNode(element);
423 if (browser_plugin) {
424 dst->AddContentIntAttribute(
425 AX_CONTENT_ATTR_CHILD_BROWSER_PLUGIN_INSTANCE_ID,
426 browser_plugin->browser_plugin_instance_id());
429 // Out-of-process iframe.
430 if (is_iframe) {
431 WebFrame* frame = WebFrame::fromFrameOwnerElement(element);
433 if (frame && frame->isWebRemoteFrame()) {
434 RenderFrameProxy* render_frame_proxy =
435 RenderFrameProxy::FromWebFrame(frame);
436 DCHECK(render_frame_proxy);
437 dst->AddContentIntAttribute(
438 AX_CONTENT_ATTR_CHILD_ROUTING_ID,
439 render_frame_proxy->routing_id());
444 if (src.isInLiveRegion()) {
445 dst->AddBoolAttribute(ui::AX_ATTR_LIVE_ATOMIC, src.liveRegionAtomic());
446 dst->AddBoolAttribute(ui::AX_ATTR_LIVE_BUSY, src.liveRegionBusy());
447 if (src.liveRegionBusy())
448 dst->state |= (1 << ui::AX_STATE_BUSY);
449 if (!src.liveRegionStatus().isEmpty()) {
450 dst->AddStringAttribute(
451 ui::AX_ATTR_LIVE_STATUS,
452 UTF16ToUTF8(base::StringPiece16(src.liveRegionStatus())));
454 dst->AddStringAttribute(
455 ui::AX_ATTR_LIVE_RELEVANT,
456 UTF16ToUTF8(base::StringPiece16(src.liveRegionRelevant())));
457 dst->AddBoolAttribute(ui::AX_ATTR_CONTAINER_LIVE_ATOMIC,
458 src.containerLiveRegionAtomic());
459 dst->AddBoolAttribute(ui::AX_ATTR_CONTAINER_LIVE_BUSY,
460 src.containerLiveRegionBusy());
461 dst->AddStringAttribute(
462 ui::AX_ATTR_CONTAINER_LIVE_STATUS,
463 UTF16ToUTF8(base::StringPiece16(src.containerLiveRegionStatus())));
464 dst->AddStringAttribute(
465 ui::AX_ATTR_CONTAINER_LIVE_RELEVANT,
466 UTF16ToUTF8(base::StringPiece16(src.containerLiveRegionRelevant())));
469 if (dst->role == ui::AX_ROLE_PROGRESS_INDICATOR ||
470 dst->role == ui::AX_ROLE_METER ||
471 dst->role == ui::AX_ROLE_SCROLL_BAR ||
472 dst->role == ui::AX_ROLE_SLIDER ||
473 dst->role == ui::AX_ROLE_SPIN_BUTTON) {
474 dst->AddFloatAttribute(ui::AX_ATTR_VALUE_FOR_RANGE, src.valueForRange());
475 dst->AddFloatAttribute(ui::AX_ATTR_MAX_VALUE_FOR_RANGE,
476 src.maxValueForRange());
477 dst->AddFloatAttribute(ui::AX_ATTR_MIN_VALUE_FOR_RANGE,
478 src.minValueForRange());
481 if (dst->role == ui::AX_ROLE_WEB_AREA) {
482 dst->AddStringAttribute(ui::AX_ATTR_HTML_TAG, "#document");
483 const WebDocument& document = src.document();
484 if (name.empty())
485 name = UTF16ToUTF8(base::StringPiece16(document.title()));
486 dst->AddStringAttribute(
487 ui::AX_ATTR_DOC_TITLE,
488 UTF16ToUTF8(base::StringPiece16(document.title())));
489 dst->AddStringAttribute(ui::AX_ATTR_DOC_URL, document.url().spec());
490 dst->AddStringAttribute(
491 ui::AX_ATTR_DOC_MIMETYPE,
492 document.isXHTMLDocument() ? "text/xhtml" : "text/html");
493 dst->AddBoolAttribute(ui::AX_ATTR_DOC_LOADED, src.isLoaded());
494 dst->AddFloatAttribute(ui::AX_ATTR_DOC_LOADING_PROGRESS,
495 src.estimatedLoadingProgress());
497 const WebDocumentType& doctype = document.doctype();
498 if (!doctype.isNull()) {
499 dst->AddStringAttribute(
500 ui::AX_ATTR_DOC_DOCTYPE,
501 UTF16ToUTF8(base::StringPiece16(doctype.name())));
504 WebAXObject anchor_object, focus_object;
505 int anchor_offset, focus_offset;
506 src.selection(anchor_object, anchor_offset, focus_object, focus_offset);
507 if (!anchor_object.isNull() && !focus_object.isNull() &&
508 anchor_offset >= 0 && focus_offset >= 0) {
509 int32 anchor_id = anchor_object.axID();
510 int32 focus_id = focus_object.axID();
511 dst->AddIntAttribute(ui::AX_ATTR_ANCHOR_OBJECT_ID, anchor_id);
512 dst->AddIntAttribute(ui::AX_ATTR_ANCHOR_OFFSET, anchor_offset);
513 dst->AddIntAttribute(ui::AX_ATTR_FOCUS_OBJECT_ID, focus_id);
514 dst->AddIntAttribute(ui::AX_ATTR_FOCUS_OFFSET, focus_offset);
517 // Get the tree ID for this frame and possibly the parent frame.
518 WebLocalFrame* web_frame = document.frame();
519 if (web_frame) {
520 RenderFrame* render_frame = RenderFrame::FromWebFrame(web_frame);
521 dst->AddContentIntAttribute(
522 AX_CONTENT_ATTR_ROUTING_ID,
523 render_frame->GetRoutingID());
525 // Get the tree ID for the parent frame, if it's remote.
526 // (If it's local, it's already part of this same tree.)
527 blink::WebFrame* parent_web_frame = web_frame->parent();
528 if (parent_web_frame && parent_web_frame->isWebRemoteFrame()) {
529 RenderFrameProxy* parent_render_frame_proxy =
530 RenderFrameProxy::FromWebFrame(parent_web_frame);
531 dst->AddContentIntAttribute(
532 AX_CONTENT_ATTR_PARENT_ROUTING_ID,
533 parent_render_frame_proxy->routing_id());
538 if (dst->role == ui::AX_ROLE_TABLE) {
539 int column_count = src.columnCount();
540 int row_count = src.rowCount();
541 if (column_count > 0 && row_count > 0) {
542 std::set<int32> unique_cell_id_set;
543 std::vector<int32> cell_ids;
544 std::vector<int32> unique_cell_ids;
545 dst->AddIntAttribute(ui::AX_ATTR_TABLE_COLUMN_COUNT, column_count);
546 dst->AddIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, row_count);
547 WebAXObject header = src.headerContainerObject();
548 if (!header.isDetached())
549 dst->AddIntAttribute(ui::AX_ATTR_TABLE_HEADER_ID, header.axID());
550 for (int i = 0; i < column_count * row_count; ++i) {
551 WebAXObject cell = src.cellForColumnAndRow(
552 i % column_count, i / column_count);
553 int cell_id = -1;
554 if (!cell.isDetached()) {
555 cell_id = cell.axID();
556 if (unique_cell_id_set.find(cell_id) == unique_cell_id_set.end()) {
557 unique_cell_id_set.insert(cell_id);
558 unique_cell_ids.push_back(cell_id);
561 cell_ids.push_back(cell_id);
563 dst->AddIntListAttribute(ui::AX_ATTR_CELL_IDS, cell_ids);
564 dst->AddIntListAttribute(ui::AX_ATTR_UNIQUE_CELL_IDS, unique_cell_ids);
568 if (dst->role == ui::AX_ROLE_ROW) {
569 dst->AddIntAttribute(ui::AX_ATTR_TABLE_ROW_INDEX, src.rowIndex());
570 WebAXObject header = src.rowHeader();
571 if (!header.isDetached())
572 dst->AddIntAttribute(ui::AX_ATTR_TABLE_ROW_HEADER_ID, header.axID());
575 if (dst->role == ui::AX_ROLE_COLUMN) {
576 dst->AddIntAttribute(ui::AX_ATTR_TABLE_COLUMN_INDEX, src.columnIndex());
577 WebAXObject header = src.columnHeader();
578 if (!header.isDetached())
579 dst->AddIntAttribute(ui::AX_ATTR_TABLE_COLUMN_HEADER_ID, header.axID());
582 if (dst->role == ui::AX_ROLE_CELL ||
583 dst->role == ui::AX_ROLE_ROW_HEADER ||
584 dst->role == ui::AX_ROLE_COLUMN_HEADER) {
585 dst->AddIntAttribute(ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX,
586 src.cellColumnIndex());
587 dst->AddIntAttribute(ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN,
588 src.cellColumnSpan());
589 dst->AddIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX, src.cellRowIndex());
590 dst->AddIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_SPAN, src.cellRowSpan());
593 if ((dst->role == ui::AX_ROLE_ROW_HEADER ||
594 dst->role == ui::AX_ROLE_COLUMN_HEADER) && src.sortDirection()) {
595 dst->AddIntAttribute(ui::AX_ATTR_SORT_DIRECTION,
596 AXSortDirectionFromBlink(src.sortDirection()));
599 dst->AddStringAttribute(ui::AX_ATTR_NAME, name);
601 // Add the ids of *indirect* children - those who are children of this node,
602 // but whose parent is *not* this node. One example is a table
603 // cell, which is a child of both a row and a column. Because the cell's
604 // parent is the row, the row adds it as a child, and the column adds it
605 // as an indirect child.
606 int child_count = src.childCount();
607 for (int i = 0; i < child_count; ++i) {
608 WebAXObject child = src.childAt(i);
609 std::vector<int32> indirect_child_ids;
610 if (!is_iframe && !child.isDetached() && !IsParentUnignoredOf(src, child))
611 indirect_child_ids.push_back(child.axID());
612 if (indirect_child_ids.size() > 0) {
613 dst->AddIntListAttribute(
614 ui::AX_ATTR_INDIRECT_CHILD_IDS, indirect_child_ids);
618 WebVector<WebAXObject> controls;
619 if (src.ariaControls(controls))
620 AddIntListAttributeFromWebObjects(ui::AX_ATTR_CONTROLS_IDS, controls, dst);
622 WebVector<WebAXObject> describedby;
623 if (src.deprecatedAriaDescribedby(describedby)) {
624 AddIntListAttributeFromWebObjects(
625 ui::AX_ATTR_DESCRIBEDBY_IDS, describedby, dst);
628 WebVector<WebAXObject> flowTo;
629 if (src.ariaFlowTo(flowTo))
630 AddIntListAttributeFromWebObjects(ui::AX_ATTR_FLOWTO_IDS, flowTo, dst);
632 WebVector<WebAXObject> labelledby;
633 if (src.deprecatedAriaLabelledby(labelledby)) {
634 AddIntListAttributeFromWebObjects(
635 ui::AX_ATTR_LABELLEDBY_IDS, labelledby, dst);
638 WebVector<WebAXObject> owns;
639 if (src.ariaOwns(owns))
640 AddIntListAttributeFromWebObjects(ui::AX_ATTR_OWNS_IDS, owns, dst);
643 if (src.isScrollableContainer()) {
644 const gfx::Point& scrollOffset = src.scrollOffset();
645 dst->AddIntAttribute(ui::AX_ATTR_SCROLL_X, scrollOffset.x());
646 dst->AddIntAttribute(ui::AX_ATTR_SCROLL_Y, scrollOffset.y());
648 const gfx::Point& minScrollOffset = src.minimumScrollOffset();
649 dst->AddIntAttribute(ui::AX_ATTR_SCROLL_X_MIN, minScrollOffset.x());
650 dst->AddIntAttribute(ui::AX_ATTR_SCROLL_Y_MIN, minScrollOffset.y());
652 const gfx::Point& maxScrollOffset = src.maximumScrollOffset();
653 dst->AddIntAttribute(ui::AX_ATTR_SCROLL_X_MAX, maxScrollOffset.x());
654 dst->AddIntAttribute(ui::AX_ATTR_SCROLL_Y_MAX, maxScrollOffset.y());
658 blink::WebDocument BlinkAXTreeSource::GetMainDocument() const {
659 if (render_frame_ && render_frame_->GetWebFrame())
660 return render_frame_->GetWebFrame()->document();
661 return WebDocument();
664 } // namespace content