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"
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/render_view_impl.h"
14 #include "third_party/WebKit/public/platform/WebRect.h"
15 #include "third_party/WebKit/public/platform/WebSize.h"
16 #include "third_party/WebKit/public/platform/WebString.h"
17 #include "third_party/WebKit/public/platform/WebVector.h"
18 #include "third_party/WebKit/public/web/WebAXEnums.h"
19 #include "third_party/WebKit/public/web/WebAXObject.h"
20 #include "third_party/WebKit/public/web/WebDocument.h"
21 #include "third_party/WebKit/public/web/WebDocumentType.h"
22 #include "third_party/WebKit/public/web/WebElement.h"
23 #include "third_party/WebKit/public/web/WebFormControlElement.h"
24 #include "third_party/WebKit/public/web/WebInputElement.h"
25 #include "third_party/WebKit/public/web/WebLocalFrame.h"
26 #include "third_party/WebKit/public/web/WebNode.h"
27 #include "third_party/WebKit/public/web/WebView.h"
29 using base::ASCIIToUTF16
;
30 using base::UTF16ToUTF8
;
31 using blink::WebAXObject
;
32 using blink::WebDocument
;
33 using blink::WebDocumentType
;
34 using blink::WebElement
;
35 using blink::WebLocalFrame
;
37 using blink::WebVector
;
44 // Returns true if |ancestor| is the first unignored parent of |child|,
45 // which means that when walking up the parent chain from |child|,
46 // |ancestor| is the *first* ancestor that isn't marked as
47 // accessibilityIsIgnored().
48 bool IsParentUnignoredOf(WebAXObject ancestor
,
50 WebAXObject parent
= child
.parentObject();
51 while (!parent
.isDetached() && parent
.accessibilityIsIgnored())
52 parent
= parent
.parentObject();
53 return parent
.equals(ancestor
);
56 bool IsTrue(std::string html_value
) {
57 return LowerCaseEqualsASCII(html_value
, "true");
60 std::string
GetEquivalentAriaRoleString(const ui::AXRole role
) {
62 case ui::AX_ROLE_ARTICLE
:
64 case ui::AX_ROLE_BANNER
:
66 case ui::AX_ROLE_COMPLEMENTARY
:
67 return "complementary";
68 case ui::AX_ROLE_CONTENT_INFO
:
69 case ui::AX_ROLE_FOOTER
:
71 case ui::AX_ROLE_MAIN
:
73 case ui::AX_ROLE_NAVIGATION
:
75 case ui::AX_ROLE_REGION
:
84 void AddIntListAttributeFromWebObjects(ui::AXIntListAttribute attr
,
85 WebVector
<WebAXObject
> objects
,
86 ui::AXNodeData
* dst
) {
87 std::vector
<int32
> ids
;
88 for(size_t i
= 0; i
< objects
.size(); i
++)
89 ids
.push_back(objects
[i
].axID());
91 dst
->AddIntListAttribute(attr
, ids
);
94 } // Anonymous namespace
96 BlinkAXTreeSource::BlinkAXTreeSource(RenderViewImpl
* render_view
)
97 : render_view_(render_view
) {
100 BlinkAXTreeSource::~BlinkAXTreeSource() {
103 bool BlinkAXTreeSource::IsInTree(blink::WebAXObject node
) const {
104 const blink::WebAXObject
& root
= GetRoot();
105 while (IsValid(node
)) {
106 if (node
.equals(root
))
108 node
= GetParent(node
);
113 blink::WebAXObject
BlinkAXTreeSource::GetRoot() const {
114 return GetMainDocument().accessibilityObject();
117 blink::WebAXObject
BlinkAXTreeSource::GetFromId(int32 id
) const {
118 return GetMainDocument().accessibilityObjectFromID(id
);
121 int32
BlinkAXTreeSource::GetId(blink::WebAXObject node
) const {
125 void BlinkAXTreeSource::GetChildren(
126 blink::WebAXObject parent
,
127 std::vector
<blink::WebAXObject
>* out_children
) const {
128 bool is_iframe
= false;
129 WebNode node
= parent
.node();
130 if (!node
.isNull() && node
.isElementNode()) {
131 WebElement element
= node
.to
<WebElement
>();
132 is_iframe
= (element
.tagName() == ASCIIToUTF16("IFRAME"));
135 for (unsigned i
= 0; i
< parent
.childCount(); i
++) {
136 blink::WebAXObject child
= parent
.childAt(i
);
138 // The child may be invalid due to issues in blink accessibility code.
139 if (child
.isDetached())
142 // Skip children whose parent isn't |parent|.
143 // As an exception, include children of an iframe element.
144 if (!is_iframe
&& !IsParentUnignoredOf(parent
, child
))
147 out_children
->push_back(child
);
151 blink::WebAXObject
BlinkAXTreeSource::GetParent(
152 blink::WebAXObject node
) const {
153 // Blink returns ignored objects when walking up the parent chain,
154 // we have to skip those here. Also, stop when we get to the root
156 blink::WebAXObject root
= GetRoot();
158 if (node
.equals(root
))
159 return blink::WebAXObject();
160 node
= node
.parentObject();
161 } while (!node
.isDetached() && node
.accessibilityIsIgnored());
166 bool BlinkAXTreeSource::IsValid(blink::WebAXObject node
) const {
167 return !node
.isDetached(); // This also checks if it's null.
170 bool BlinkAXTreeSource::IsEqual(blink::WebAXObject node1
,
171 blink::WebAXObject node2
) const {
172 return node1
.equals(node2
);
175 blink::WebAXObject
BlinkAXTreeSource::GetNull() const {
176 return blink::WebAXObject();
179 void BlinkAXTreeSource::SerializeNode(blink::WebAXObject src
,
180 ui::AXNodeData
* dst
) const {
181 dst
->role
= AXRoleFromBlink(src
.role());
182 dst
->state
= AXStateFromBlink(src
);
183 dst
->location
= src
.boundingBoxRect();
184 dst
->id
= src
.axID();
185 std::string name
= UTF16ToUTF8(src
.title());
188 if (src
.valueDescription().length()) {
189 dst
->AddStringAttribute(ui::AX_ATTR_VALUE
,
190 UTF16ToUTF8(src
.valueDescription()));
192 dst
->AddStringAttribute(ui::AX_ATTR_VALUE
, UTF16ToUTF8(src
.stringValue()));
195 if (dst
->role
== ui::AX_ROLE_COLOR_WELL
) {
197 src
.colorValue(r
, g
, b
);
198 dst
->AddIntAttribute(ui::AX_ATTR_COLOR_VALUE_RED
, r
);
199 dst
->AddIntAttribute(ui::AX_ATTR_COLOR_VALUE_GREEN
, g
);
200 dst
->AddIntAttribute(ui::AX_ATTR_COLOR_VALUE_BLUE
, b
);
203 if (dst
->role
== ui::AX_ROLE_INLINE_TEXT_BOX
) {
204 dst
->AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION
,
205 AXTextDirectionFromBlink(src
.textDirection()));
207 WebVector
<int> src_character_offsets
;
208 src
.characterOffsets(src_character_offsets
);
209 std::vector
<int32
> character_offsets
;
210 character_offsets
.reserve(src_character_offsets
.size());
211 for (size_t i
= 0; i
< src_character_offsets
.size(); ++i
)
212 character_offsets
.push_back(src_character_offsets
[i
]);
213 dst
->AddIntListAttribute(ui::AX_ATTR_CHARACTER_OFFSETS
, character_offsets
);
215 WebVector
<int> src_word_starts
;
216 WebVector
<int> src_word_ends
;
217 src
.wordBoundaries(src_word_starts
, src_word_ends
);
218 std::vector
<int32
> word_starts
;
219 std::vector
<int32
> word_ends
;
220 word_starts
.reserve(src_word_starts
.size());
221 word_ends
.reserve(src_word_starts
.size());
222 for (size_t i
= 0; i
< src_word_starts
.size(); ++i
) {
223 word_starts
.push_back(src_word_starts
[i
]);
224 word_ends
.push_back(src_word_ends
[i
]);
226 dst
->AddIntListAttribute(ui::AX_ATTR_WORD_STARTS
, word_starts
);
227 dst
->AddIntListAttribute(ui::AX_ATTR_WORD_ENDS
, word_ends
);
230 if (src
.accessKey().length()) {
231 dst
->AddStringAttribute(ui::AX_ATTR_ACCESS_KEY
,
232 UTF16ToUTF8(src
.accessKey()));
234 if (src
.actionVerb().length())
235 dst
->AddStringAttribute(ui::AX_ATTR_ACTION
, UTF16ToUTF8(src
.actionVerb()));
236 if (src
.isAriaReadOnly())
237 dst
->AddBoolAttribute(ui::AX_ATTR_ARIA_READONLY
, true);
238 if (src
.isButtonStateMixed())
239 dst
->AddBoolAttribute(ui::AX_ATTR_BUTTON_MIXED
, true);
240 if (src
.canSetValueAttribute())
241 dst
->AddBoolAttribute(ui::AX_ATTR_CAN_SET_VALUE
, true);
242 if (src
.accessibilityDescription().length()) {
243 dst
->AddStringAttribute(ui::AX_ATTR_DESCRIPTION
,
244 UTF16ToUTF8(src
.accessibilityDescription()));
246 if (src
.hasComputedStyle()) {
247 dst
->AddStringAttribute(ui::AX_ATTR_DISPLAY
,
248 UTF16ToUTF8(src
.computedStyleDisplay()));
250 if (src
.helpText().length())
251 dst
->AddStringAttribute(ui::AX_ATTR_HELP
, UTF16ToUTF8(src
.helpText()));
252 if (src
.keyboardShortcut().length()) {
253 dst
->AddStringAttribute(ui::AX_ATTR_SHORTCUT
,
254 UTF16ToUTF8(src
.keyboardShortcut()));
256 if (!src
.titleUIElement().isDetached()) {
257 dst
->AddIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT
,
258 src
.titleUIElement().axID());
260 if (!src
.ariaActiveDescendant().isDetached()) {
261 dst
->AddIntAttribute(ui::AX_ATTR_ACTIVEDESCENDANT_ID
,
262 src
.ariaActiveDescendant().axID());
265 if (!src
.url().isEmpty())
266 dst
->AddStringAttribute(ui::AX_ATTR_URL
, src
.url().spec());
268 if (dst
->role
== ui::AX_ROLE_HEADING
)
269 dst
->AddIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL
, src
.headingLevel());
270 else if ((dst
->role
== ui::AX_ROLE_TREE_ITEM
||
271 dst
->role
== ui::AX_ROLE_ROW
) &&
272 src
.hierarchicalLevel() > 0) {
273 dst
->AddIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL
,
274 src
.hierarchicalLevel());
277 // Treat the active list box item as focused.
278 if (dst
->role
== ui::AX_ROLE_LIST_BOX_OPTION
&&
279 src
.isSelectedOptionActive()) {
280 dst
->state
|= (1 << ui::AX_STATE_FOCUSED
);
283 if (src
.canvasHasFallbackContent())
284 dst
->AddBoolAttribute(ui::AX_ATTR_CANVAS_HAS_FALLBACK
, true);
286 WebNode node
= src
.node();
287 bool is_iframe
= false;
288 std::string live_atomic
;
289 std::string live_busy
;
290 std::string live_status
;
291 std::string live_relevant
;
293 if (!node
.isNull() && node
.isElementNode()) {
294 WebElement element
= node
.to
<WebElement
>();
295 is_iframe
= (element
.tagName() == ASCIIToUTF16("IFRAME"));
297 if (LowerCaseEqualsASCII(element
.getAttribute("aria-expanded"), "true"))
298 dst
->state
|= (1 << ui::AX_STATE_EXPANDED
);
300 // TODO(ctguil): The tagName in WebKit is lower cased but
301 // HTMLElement::nodeName calls localNameUpper. Consider adding
302 // a WebElement method that returns the original lower cased tagName.
303 dst
->AddStringAttribute(
304 ui::AX_ATTR_HTML_TAG
,
305 StringToLowerASCII(UTF16ToUTF8(element
.tagName())));
306 for (unsigned i
= 0; i
< element
.attributeCount(); ++i
) {
307 std::string name
= StringToLowerASCII(UTF16ToUTF8(
308 element
.attributeLocalName(i
)));
309 std::string value
= UTF16ToUTF8(element
.attributeValue(i
));
310 dst
->html_attributes
.push_back(std::make_pair(name
, value
));
313 if (dst
->role
== ui::AX_ROLE_EDITABLE_TEXT
||
314 dst
->role
== ui::AX_ROLE_TEXT_AREA
||
315 dst
->role
== ui::AX_ROLE_TEXT_FIELD
) {
316 dst
->AddIntAttribute(ui::AX_ATTR_TEXT_SEL_START
, src
.selectionStart());
317 dst
->AddIntAttribute(ui::AX_ATTR_TEXT_SEL_END
, src
.selectionEnd());
319 WebVector
<int> src_line_breaks
;
320 src
.lineBreaks(src_line_breaks
);
321 if (src_line_breaks
.size() > 0) {
322 std::vector
<int32
> line_breaks
;
323 line_breaks
.reserve(src_line_breaks
.size());
324 for (size_t i
= 0; i
< src_line_breaks
.size(); ++i
)
325 line_breaks
.push_back(src_line_breaks
[i
]);
326 dst
->AddIntListAttribute(ui::AX_ATTR_LINE_BREAKS
, line_breaks
);
331 if (element
.hasAttribute("role")) {
332 dst
->AddStringAttribute(ui::AX_ATTR_ROLE
,
333 UTF16ToUTF8(element
.getAttribute("role")));
335 std::string role
= GetEquivalentAriaRoleString(dst
->role
);
337 dst
->AddStringAttribute(ui::AX_ATTR_ROLE
, role
);
340 // Live region attributes
341 live_atomic
= UTF16ToUTF8(element
.getAttribute("aria-atomic"));
342 live_busy
= UTF16ToUTF8(element
.getAttribute("aria-busy"));
343 live_status
= UTF16ToUTF8(element
.getAttribute("aria-live"));
344 live_relevant
= UTF16ToUTF8(element
.getAttribute("aria-relevant"));
347 // Walk up the parent chain to set live region attributes of containers
348 std::string container_live_atomic
;
349 std::string container_live_busy
;
350 std::string container_live_status
;
351 std::string container_live_relevant
;
352 WebAXObject container_accessible
= src
;
353 while (!container_accessible
.isDetached()) {
354 WebNode container_node
= container_accessible
.node();
355 if (!container_node
.isNull() && container_node
.isElementNode()) {
356 WebElement container_elem
= container_node
.to
<WebElement
>();
357 if (container_elem
.hasAttribute("aria-atomic") &&
358 container_live_atomic
.empty()) {
359 container_live_atomic
=
360 UTF16ToUTF8(container_elem
.getAttribute("aria-atomic"));
362 if (container_elem
.hasAttribute("aria-busy") &&
363 container_live_busy
.empty()) {
364 container_live_busy
=
365 UTF16ToUTF8(container_elem
.getAttribute("aria-busy"));
367 if (container_elem
.hasAttribute("aria-live") &&
368 container_live_status
.empty()) {
369 container_live_status
=
370 UTF16ToUTF8(container_elem
.getAttribute("aria-live"));
372 if (container_elem
.hasAttribute("aria-relevant") &&
373 container_live_relevant
.empty()) {
374 container_live_relevant
=
375 UTF16ToUTF8(container_elem
.getAttribute("aria-relevant"));
378 container_accessible
= container_accessible
.parentObject();
381 if (!live_atomic
.empty())
382 dst
->AddBoolAttribute(ui::AX_ATTR_LIVE_ATOMIC
, IsTrue(live_atomic
));
383 if (!live_busy
.empty())
384 dst
->AddBoolAttribute(ui::AX_ATTR_LIVE_BUSY
, IsTrue(live_busy
));
385 if (!live_status
.empty())
386 dst
->AddStringAttribute(ui::AX_ATTR_LIVE_STATUS
, live_status
);
387 if (!live_relevant
.empty())
388 dst
->AddStringAttribute(ui::AX_ATTR_LIVE_RELEVANT
, live_relevant
);
390 if (!container_live_atomic
.empty()) {
391 dst
->AddBoolAttribute(ui::AX_ATTR_CONTAINER_LIVE_ATOMIC
,
392 IsTrue(container_live_atomic
));
394 if (!container_live_busy
.empty()) {
395 dst
->AddBoolAttribute(ui::AX_ATTR_CONTAINER_LIVE_BUSY
,
396 IsTrue(container_live_busy
));
398 if (!container_live_status
.empty()) {
399 dst
->AddStringAttribute(ui::AX_ATTR_CONTAINER_LIVE_STATUS
,
400 container_live_status
);
402 if (!container_live_relevant
.empty()) {
403 dst
->AddStringAttribute(ui::AX_ATTR_CONTAINER_LIVE_RELEVANT
,
404 container_live_relevant
);
407 if (dst
->role
== ui::AX_ROLE_PROGRESS_INDICATOR
||
408 dst
->role
== ui::AX_ROLE_SCROLL_BAR
||
409 dst
->role
== ui::AX_ROLE_SLIDER
||
410 dst
->role
== ui::AX_ROLE_SPIN_BUTTON
) {
411 dst
->AddFloatAttribute(ui::AX_ATTR_VALUE_FOR_RANGE
, src
.valueForRange());
412 dst
->AddFloatAttribute(ui::AX_ATTR_MAX_VALUE_FOR_RANGE
,
413 src
.maxValueForRange());
414 dst
->AddFloatAttribute(ui::AX_ATTR_MIN_VALUE_FOR_RANGE
,
415 src
.minValueForRange());
418 if (dst
->role
== ui::AX_ROLE_DOCUMENT
||
419 dst
->role
== ui::AX_ROLE_WEB_AREA
) {
420 dst
->AddStringAttribute(ui::AX_ATTR_HTML_TAG
, "#document");
421 const WebDocument
& document
= src
.document();
423 name
= UTF16ToUTF8(document
.title());
424 dst
->AddStringAttribute(ui::AX_ATTR_DOC_TITLE
,
425 UTF16ToUTF8(document
.title()));
426 dst
->AddStringAttribute(ui::AX_ATTR_DOC_URL
, document
.url().spec());
427 dst
->AddStringAttribute(
428 ui::AX_ATTR_DOC_MIMETYPE
,
429 document
.isXHTMLDocument() ? "text/xhtml" : "text/html");
430 dst
->AddBoolAttribute(ui::AX_ATTR_DOC_LOADED
, src
.isLoaded());
431 dst
->AddFloatAttribute(ui::AX_ATTR_DOC_LOADING_PROGRESS
,
432 src
.estimatedLoadingProgress());
434 const WebDocumentType
& doctype
= document
.doctype();
435 if (!doctype
.isNull()) {
436 dst
->AddStringAttribute(ui::AX_ATTR_DOC_DOCTYPE
,
437 UTF16ToUTF8(doctype
.name()));
440 const gfx::Size
& scroll_offset
= document
.frame()->scrollOffset();
441 dst
->AddIntAttribute(ui::AX_ATTR_SCROLL_X
, scroll_offset
.width());
442 dst
->AddIntAttribute(ui::AX_ATTR_SCROLL_Y
, scroll_offset
.height());
444 const gfx::Size
& min_offset
= document
.frame()->minimumScrollOffset();
445 dst
->AddIntAttribute(ui::AX_ATTR_SCROLL_X_MIN
, min_offset
.width());
446 dst
->AddIntAttribute(ui::AX_ATTR_SCROLL_Y_MIN
, min_offset
.height());
448 const gfx::Size
& max_offset
= document
.frame()->maximumScrollOffset();
449 dst
->AddIntAttribute(ui::AX_ATTR_SCROLL_X_MAX
, max_offset
.width());
450 dst
->AddIntAttribute(ui::AX_ATTR_SCROLL_Y_MAX
, max_offset
.height());
453 if (dst
->role
== ui::AX_ROLE_TABLE
) {
454 int column_count
= src
.columnCount();
455 int row_count
= src
.rowCount();
456 if (column_count
> 0 && row_count
> 0) {
457 std::set
<int32
> unique_cell_id_set
;
458 std::vector
<int32
> cell_ids
;
459 std::vector
<int32
> unique_cell_ids
;
460 dst
->AddIntAttribute(ui::AX_ATTR_TABLE_COLUMN_COUNT
, column_count
);
461 dst
->AddIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT
, row_count
);
462 WebAXObject header
= src
.headerContainerObject();
463 if (!header
.isDetached())
464 dst
->AddIntAttribute(ui::AX_ATTR_TABLE_HEADER_ID
, header
.axID());
465 for (int i
= 0; i
< column_count
* row_count
; ++i
) {
466 WebAXObject cell
= src
.cellForColumnAndRow(
467 i
% column_count
, i
/ column_count
);
469 if (!cell
.isDetached()) {
470 cell_id
= cell
.axID();
471 if (unique_cell_id_set
.find(cell_id
) == unique_cell_id_set
.end()) {
472 unique_cell_id_set
.insert(cell_id
);
473 unique_cell_ids
.push_back(cell_id
);
476 cell_ids
.push_back(cell_id
);
478 dst
->AddIntListAttribute(ui::AX_ATTR_CELL_IDS
, cell_ids
);
479 dst
->AddIntListAttribute(ui::AX_ATTR_UNIQUE_CELL_IDS
, unique_cell_ids
);
483 if (dst
->role
== ui::AX_ROLE_ROW
) {
484 dst
->AddIntAttribute(ui::AX_ATTR_TABLE_ROW_INDEX
, src
.rowIndex());
485 WebAXObject header
= src
.rowHeader();
486 if (!header
.isDetached())
487 dst
->AddIntAttribute(ui::AX_ATTR_TABLE_ROW_HEADER_ID
, header
.axID());
490 if (dst
->role
== ui::AX_ROLE_COLUMN
) {
491 dst
->AddIntAttribute(ui::AX_ATTR_TABLE_COLUMN_INDEX
, src
.columnIndex());
492 WebAXObject header
= src
.columnHeader();
493 if (!header
.isDetached())
494 dst
->AddIntAttribute(ui::AX_ATTR_TABLE_COLUMN_HEADER_ID
, header
.axID());
497 if (dst
->role
== ui::AX_ROLE_CELL
||
498 dst
->role
== ui::AX_ROLE_ROW_HEADER
||
499 dst
->role
== ui::AX_ROLE_COLUMN_HEADER
) {
500 dst
->AddIntAttribute(ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX
,
501 src
.cellColumnIndex());
502 dst
->AddIntAttribute(ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN
,
503 src
.cellColumnSpan());
504 dst
->AddIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX
, src
.cellRowIndex());
505 dst
->AddIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_SPAN
, src
.cellRowSpan());
508 dst
->AddStringAttribute(ui::AX_ATTR_NAME
, name
);
510 // Add the ids of *indirect* children - those who are children of this node,
511 // but whose parent is *not* this node. One example is a table
512 // cell, which is a child of both a row and a column. Because the cell's
513 // parent is the row, the row adds it as a child, and the column adds it
514 // as an indirect child.
515 int child_count
= src
.childCount();
516 for (int i
= 0; i
< child_count
; ++i
) {
517 WebAXObject child
= src
.childAt(i
);
518 std::vector
<int32
> indirect_child_ids
;
519 if (!is_iframe
&& !child
.isDetached() && !IsParentUnignoredOf(src
, child
))
520 indirect_child_ids
.push_back(child
.axID());
521 if (indirect_child_ids
.size() > 0) {
522 dst
->AddIntListAttribute(
523 ui::AX_ATTR_INDIRECT_CHILD_IDS
, indirect_child_ids
);
527 WebVector
<WebAXObject
> controls
;
528 if (src
.ariaControls(controls
))
529 AddIntListAttributeFromWebObjects(ui::AX_ATTR_CONTROLS_IDS
, controls
, dst
);
531 WebVector
<WebAXObject
> describedby
;
532 if (src
.ariaDescribedby(describedby
)) {
533 AddIntListAttributeFromWebObjects(
534 ui::AX_ATTR_DESCRIBEDBY_IDS
, describedby
, dst
);
537 WebVector
<WebAXObject
> flowTo
;
538 if (src
.ariaFlowTo(flowTo
))
539 AddIntListAttributeFromWebObjects(ui::AX_ATTR_FLOWTO_IDS
, flowTo
, dst
);
541 WebVector
<WebAXObject
> labelledby
;
542 if (src
.ariaLabelledby(labelledby
)) {
543 AddIntListAttributeFromWebObjects(
544 ui::AX_ATTR_LABELLEDBY_IDS
, labelledby
, dst
);
547 WebVector
<WebAXObject
> owns
;
548 if (src
.ariaOwns(owns
))
549 AddIntListAttributeFromWebObjects(ui::AX_ATTR_OWNS_IDS
, owns
, dst
);
552 blink::WebDocument
BlinkAXTreeSource::GetMainDocument() const {
553 WebView
* view
= render_view_
->GetWebView();
554 WebLocalFrame
* main_frame
=
555 view
? view
->mainFrame()->toWebLocalFrame() : NULL
;
558 return main_frame
->document();
560 return WebDocument();
563 } // namespace content