OLD | NEW |
(Empty) | |
| 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. |
| 4 |
| 5 #include "content/renderer/accessibility/accessibility_node_serializer.h" |
| 6 |
| 7 #include <set> |
| 8 |
| 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 "third_party/WebKit/public/platform/WebRect.h" |
| 14 #include "third_party/WebKit/public/platform/WebSize.h" |
| 15 #include "third_party/WebKit/public/platform/WebString.h" |
| 16 #include "third_party/WebKit/public/platform/WebVector.h" |
| 17 #include "third_party/WebKit/public/web/WebAXEnums.h" |
| 18 #include "third_party/WebKit/public/web/WebAXObject.h" |
| 19 #include "third_party/WebKit/public/web/WebDocument.h" |
| 20 #include "third_party/WebKit/public/web/WebDocumentType.h" |
| 21 #include "third_party/WebKit/public/web/WebElement.h" |
| 22 #include "third_party/WebKit/public/web/WebFormControlElement.h" |
| 23 #include "third_party/WebKit/public/web/WebFrame.h" |
| 24 #include "third_party/WebKit/public/web/WebInputElement.h" |
| 25 #include "third_party/WebKit/public/web/WebNode.h" |
| 26 |
| 27 using base::UTF16ToUTF8; |
| 28 using blink::WebAXObject; |
| 29 using blink::WebDocument; |
| 30 using blink::WebDocumentType; |
| 31 using blink::WebElement; |
| 32 using blink::WebNode; |
| 33 using blink::WebVector; |
| 34 |
| 35 namespace content { |
| 36 namespace { |
| 37 |
| 38 // Returns true if |ancestor| is the first unignored parent of |child|, |
| 39 // which means that when walking up the parent chain from |child|, |
| 40 // |ancestor| is the *first* ancestor that isn't marked as |
| 41 // accessibilityIsIgnored(). |
| 42 bool IsParentUnignoredOf(const WebAXObject& ancestor, |
| 43 const WebAXObject& child) { |
| 44 WebAXObject parent = child.parentObject(); |
| 45 while (!parent.isDetached() && parent.accessibilityIsIgnored()) |
| 46 parent = parent.parentObject(); |
| 47 return parent.equals(ancestor); |
| 48 } |
| 49 |
| 50 bool IsTrue(std::string html_value) { |
| 51 return LowerCaseEqualsASCII(html_value, "true"); |
| 52 } |
| 53 |
| 54 std::string GetEquivalentAriaRoleString(const ui::AXRole role) { |
| 55 switch (role) { |
| 56 case ui::AX_ROLE_ARTICLE: |
| 57 return "article"; |
| 58 case ui::AX_ROLE_BANNER: |
| 59 return "banner"; |
| 60 case ui::AX_ROLE_COMPLEMENTARY: |
| 61 return "complementary"; |
| 62 case ui::AX_ROLE_CONTENT_INFO: |
| 63 case ui::AX_ROLE_FOOTER: |
| 64 return "contentinfo"; |
| 65 case ui::AX_ROLE_MAIN: |
| 66 return "main"; |
| 67 case ui::AX_ROLE_NAVIGATION: |
| 68 return "navigation"; |
| 69 case ui::AX_ROLE_REGION: |
| 70 return "region"; |
| 71 default: |
| 72 break; |
| 73 } |
| 74 |
| 75 return std::string(); |
| 76 } |
| 77 |
| 78 void AddIntListAttributeFromWebObjects(ui::AXIntListAttribute attr, |
| 79 WebVector<WebAXObject> objects, |
| 80 ui::AXNodeData* dst) { |
| 81 std::vector<int32> ids; |
| 82 for(size_t i = 0; i < objects.size(); i++) |
| 83 ids.push_back(objects[i].axID()); |
| 84 if (ids.size() > 0) |
| 85 dst->AddIntListAttribute(attr, ids); |
| 86 } |
| 87 |
| 88 |
| 89 } // Anonymous namespace |
| 90 |
| 91 void SerializeAccessibilityNode( |
| 92 const WebAXObject& src, |
| 93 ui::AXNodeData* dst) { |
| 94 dst->role = AXRoleFromBlink(src.role()); |
| 95 dst->state = AXStateFromBlink(src); |
| 96 dst->location = src.boundingBoxRect(); |
| 97 dst->id = src.axID(); |
| 98 std::string name = base::UTF16ToUTF8(src.title()); |
| 99 |
| 100 std::string value; |
| 101 if (src.valueDescription().length()) { |
| 102 dst->AddStringAttribute(ui::AX_ATTR_VALUE, |
| 103 UTF16ToUTF8(src.valueDescription())); |
| 104 } else { |
| 105 dst->AddStringAttribute(ui::AX_ATTR_VALUE, UTF16ToUTF8(src.stringValue())); |
| 106 } |
| 107 |
| 108 if (dst->role == ui::AX_ROLE_COLOR_WELL) { |
| 109 int r, g, b; |
| 110 src.colorValue(r, g, b); |
| 111 dst->AddIntAttribute(ui::AX_ATTR_COLOR_VALUE_RED, r); |
| 112 dst->AddIntAttribute(ui::AX_ATTR_COLOR_VALUE_GREEN, g); |
| 113 dst->AddIntAttribute(ui::AX_ATTR_COLOR_VALUE_BLUE, b); |
| 114 } |
| 115 |
| 116 if (dst->role == ui::AX_ROLE_INLINE_TEXT_BOX) { |
| 117 dst->AddIntAttribute(ui::AX_ATTR_TEXT_DIRECTION, |
| 118 AXTextDirectionFromBlink(src.textDirection())); |
| 119 |
| 120 WebVector<int> src_character_offsets; |
| 121 src.characterOffsets(src_character_offsets); |
| 122 std::vector<int32> character_offsets; |
| 123 character_offsets.reserve(src_character_offsets.size()); |
| 124 for (size_t i = 0; i < src_character_offsets.size(); ++i) |
| 125 character_offsets.push_back(src_character_offsets[i]); |
| 126 dst->AddIntListAttribute(ui::AX_ATTR_CHARACTER_OFFSETS, character_offsets); |
| 127 |
| 128 WebVector<int> src_word_starts; |
| 129 WebVector<int> src_word_ends; |
| 130 src.wordBoundaries(src_word_starts, src_word_ends); |
| 131 std::vector<int32> word_starts; |
| 132 std::vector<int32> word_ends; |
| 133 word_starts.reserve(src_word_starts.size()); |
| 134 word_ends.reserve(src_word_starts.size()); |
| 135 for (size_t i = 0; i < src_word_starts.size(); ++i) { |
| 136 word_starts.push_back(src_word_starts[i]); |
| 137 word_ends.push_back(src_word_ends[i]); |
| 138 } |
| 139 dst->AddIntListAttribute(ui::AX_ATTR_WORD_STARTS, word_starts); |
| 140 dst->AddIntListAttribute(ui::AX_ATTR_WORD_ENDS, word_ends); |
| 141 } |
| 142 |
| 143 if (src.accessKey().length()) |
| 144 dst->AddStringAttribute(ui::AX_ATTR_ACCESS_KEY, |
| 145 UTF16ToUTF8(src.accessKey())); |
| 146 if (src.actionVerb().length()) |
| 147 dst->AddStringAttribute(ui::AX_ATTR_ACTION, UTF16ToUTF8(src.actionVerb())); |
| 148 if (src.isAriaReadOnly()) |
| 149 dst->AddBoolAttribute(ui::AX_ATTR_ARIA_READONLY, true); |
| 150 if (src.isButtonStateMixed()) |
| 151 dst->AddBoolAttribute(ui::AX_ATTR_BUTTON_MIXED, true); |
| 152 if (src.canSetValueAttribute()) |
| 153 dst->AddBoolAttribute(ui::AX_ATTR_CAN_SET_VALUE, true); |
| 154 if (src.accessibilityDescription().length()) { |
| 155 dst->AddStringAttribute(ui::AX_ATTR_DESCRIPTION, |
| 156 UTF16ToUTF8(src.accessibilityDescription())); |
| 157 } |
| 158 if (src.hasComputedStyle()) { |
| 159 dst->AddStringAttribute(ui::AX_ATTR_DISPLAY, |
| 160 UTF16ToUTF8(src.computedStyleDisplay())); |
| 161 } |
| 162 if (src.helpText().length()) |
| 163 dst->AddStringAttribute(ui::AX_ATTR_HELP, UTF16ToUTF8(src.helpText())); |
| 164 if (src.keyboardShortcut().length()) { |
| 165 dst->AddStringAttribute(ui::AX_ATTR_SHORTCUT, |
| 166 UTF16ToUTF8(src.keyboardShortcut())); |
| 167 } |
| 168 if (!src.titleUIElement().isDetached()) { |
| 169 dst->AddIntAttribute(ui::AX_ATTR_TITLE_UI_ELEMENT, |
| 170 src.titleUIElement().axID()); |
| 171 } |
| 172 if (!src.ariaActiveDescendant().isDetached()) { |
| 173 dst->AddIntAttribute(ui::AX_ATTR_ACTIVEDESCENDANT_ID, |
| 174 src.ariaActiveDescendant().axID()); |
| 175 } |
| 176 |
| 177 if (!src.url().isEmpty()) |
| 178 dst->AddStringAttribute(ui::AX_ATTR_URL, src.url().spec()); |
| 179 |
| 180 if (dst->role == ui::AX_ROLE_HEADING) |
| 181 dst->AddIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL, src.headingLevel()); |
| 182 else if ((dst->role == ui::AX_ROLE_TREE_ITEM || |
| 183 dst->role == ui::AX_ROLE_ROW) && |
| 184 src.hierarchicalLevel() > 0) { |
| 185 dst->AddIntAttribute(ui::AX_ATTR_HIERARCHICAL_LEVEL, |
| 186 src.hierarchicalLevel()); |
| 187 } |
| 188 |
| 189 // Treat the active list box item as focused. |
| 190 if (dst->role == ui::AX_ROLE_LIST_BOX_OPTION && |
| 191 src.isSelectedOptionActive()) { |
| 192 dst->state |= (1 << ui::AX_STATE_FOCUSED); |
| 193 } |
| 194 |
| 195 if (src.canvasHasFallbackContent()) |
| 196 dst->AddBoolAttribute(ui::AX_ATTR_CANVAS_HAS_FALLBACK, true); |
| 197 |
| 198 WebNode node = src.node(); |
| 199 bool is_iframe = false; |
| 200 std::string live_atomic; |
| 201 std::string live_busy; |
| 202 std::string live_status; |
| 203 std::string live_relevant; |
| 204 |
| 205 if (!node.isNull() && node.isElementNode()) { |
| 206 WebElement element = node.to<WebElement>(); |
| 207 is_iframe = (element.tagName() == base::ASCIIToUTF16("IFRAME")); |
| 208 |
| 209 if (LowerCaseEqualsASCII(element.getAttribute("aria-expanded"), "true")) |
| 210 dst->state |= (1 << ui::AX_STATE_EXPANDED); |
| 211 |
| 212 // TODO(ctguil): The tagName in WebKit is lower cased but |
| 213 // HTMLElement::nodeName calls localNameUpper. Consider adding |
| 214 // a WebElement method that returns the original lower cased tagName. |
| 215 dst->AddStringAttribute( |
| 216 ui::AX_ATTR_HTML_TAG, |
| 217 StringToLowerASCII(UTF16ToUTF8(element.tagName()))); |
| 218 for (unsigned i = 0; i < element.attributeCount(); ++i) { |
| 219 std::string name = StringToLowerASCII(base::UTF16ToUTF8( |
| 220 element.attributeLocalName(i))); |
| 221 std::string value = base::UTF16ToUTF8(element.attributeValue(i)); |
| 222 dst->html_attributes.push_back(std::make_pair(name, value)); |
| 223 } |
| 224 |
| 225 if (dst->role == ui::AX_ROLE_EDITABLE_TEXT || |
| 226 dst->role == ui::AX_ROLE_TEXT_AREA || |
| 227 dst->role == ui::AX_ROLE_TEXT_FIELD) { |
| 228 dst->AddIntAttribute(ui::AX_ATTR_TEXT_SEL_START, src.selectionStart()); |
| 229 dst->AddIntAttribute(ui::AX_ATTR_TEXT_SEL_END, src.selectionEnd()); |
| 230 |
| 231 WebVector<int> src_line_breaks; |
| 232 src.lineBreaks(src_line_breaks); |
| 233 if (src_line_breaks.size() > 0) { |
| 234 std::vector<int32> line_breaks; |
| 235 line_breaks.reserve(src_line_breaks.size()); |
| 236 for (size_t i = 0; i < src_line_breaks.size(); ++i) |
| 237 line_breaks.push_back(src_line_breaks[i]); |
| 238 dst->AddIntListAttribute(ui::AX_ATTR_LINE_BREAKS, line_breaks); |
| 239 } |
| 240 } |
| 241 |
| 242 // ARIA role. |
| 243 if (element.hasAttribute("role")) { |
| 244 dst->AddStringAttribute(ui::AX_ATTR_ROLE, |
| 245 UTF16ToUTF8(element.getAttribute("role"))); |
| 246 } else { |
| 247 std::string role = GetEquivalentAriaRoleString(dst->role); |
| 248 if (!role.empty()) |
| 249 dst->AddStringAttribute(ui::AX_ATTR_ROLE, role); |
| 250 } |
| 251 |
| 252 // Live region attributes |
| 253 live_atomic = base::UTF16ToUTF8(element.getAttribute("aria-atomic")); |
| 254 live_busy = base::UTF16ToUTF8(element.getAttribute("aria-busy")); |
| 255 live_status = base::UTF16ToUTF8(element.getAttribute("aria-live")); |
| 256 live_relevant = base::UTF16ToUTF8(element.getAttribute("aria-relevant")); |
| 257 } |
| 258 |
| 259 // Walk up the parent chain to set live region attributes of containers |
| 260 std::string container_live_atomic; |
| 261 std::string container_live_busy; |
| 262 std::string container_live_status; |
| 263 std::string container_live_relevant; |
| 264 WebAXObject container_accessible = src; |
| 265 while (!container_accessible.isDetached()) { |
| 266 WebNode container_node = container_accessible.node(); |
| 267 if (!container_node.isNull() && container_node.isElementNode()) { |
| 268 WebElement container_elem = container_node.to<WebElement>(); |
| 269 if (container_elem.hasAttribute("aria-atomic") && |
| 270 container_live_atomic.empty()) { |
| 271 container_live_atomic = |
| 272 base::UTF16ToUTF8(container_elem.getAttribute("aria-atomic")); |
| 273 } |
| 274 if (container_elem.hasAttribute("aria-busy") && |
| 275 container_live_busy.empty()) { |
| 276 container_live_busy = |
| 277 base::UTF16ToUTF8(container_elem.getAttribute("aria-busy")); |
| 278 } |
| 279 if (container_elem.hasAttribute("aria-live") && |
| 280 container_live_status.empty()) { |
| 281 container_live_status = |
| 282 base::UTF16ToUTF8(container_elem.getAttribute("aria-live")); |
| 283 } |
| 284 if (container_elem.hasAttribute("aria-relevant") && |
| 285 container_live_relevant.empty()) { |
| 286 container_live_relevant = |
| 287 base::UTF16ToUTF8(container_elem.getAttribute("aria-relevant")); |
| 288 } |
| 289 } |
| 290 container_accessible = container_accessible.parentObject(); |
| 291 } |
| 292 |
| 293 if (!live_atomic.empty()) |
| 294 dst->AddBoolAttribute(ui::AX_ATTR_LIVE_ATOMIC, IsTrue(live_atomic)); |
| 295 if (!live_busy.empty()) |
| 296 dst->AddBoolAttribute(ui::AX_ATTR_LIVE_BUSY, IsTrue(live_busy)); |
| 297 if (!live_status.empty()) |
| 298 dst->AddStringAttribute(ui::AX_ATTR_LIVE_STATUS, live_status); |
| 299 if (!live_relevant.empty()) |
| 300 dst->AddStringAttribute(ui::AX_ATTR_LIVE_RELEVANT, live_relevant); |
| 301 |
| 302 if (!container_live_atomic.empty()) { |
| 303 dst->AddBoolAttribute(ui::AX_ATTR_CONTAINER_LIVE_ATOMIC, |
| 304 IsTrue(container_live_atomic)); |
| 305 } |
| 306 if (!container_live_busy.empty()) { |
| 307 dst->AddBoolAttribute(ui::AX_ATTR_CONTAINER_LIVE_BUSY, |
| 308 IsTrue(container_live_busy)); |
| 309 } |
| 310 if (!container_live_status.empty()) { |
| 311 dst->AddStringAttribute(ui::AX_ATTR_CONTAINER_LIVE_STATUS, |
| 312 container_live_status); |
| 313 } |
| 314 if (!container_live_relevant.empty()) { |
| 315 dst->AddStringAttribute(ui::AX_ATTR_CONTAINER_LIVE_RELEVANT, |
| 316 container_live_relevant); |
| 317 } |
| 318 |
| 319 if (dst->role == ui::AX_ROLE_PROGRESS_INDICATOR || |
| 320 dst->role == ui::AX_ROLE_SCROLL_BAR || |
| 321 dst->role == ui::AX_ROLE_SLIDER || |
| 322 dst->role == ui::AX_ROLE_SPIN_BUTTON) { |
| 323 dst->AddFloatAttribute(ui::AX_ATTR_VALUE_FOR_RANGE, src.valueForRange()); |
| 324 dst->AddFloatAttribute(ui::AX_ATTR_MAX_VALUE_FOR_RANGE, |
| 325 src.maxValueForRange()); |
| 326 dst->AddFloatAttribute(ui::AX_ATTR_MIN_VALUE_FOR_RANGE, |
| 327 src.minValueForRange()); |
| 328 } |
| 329 |
| 330 if (dst->role == ui::AX_ROLE_DOCUMENT || |
| 331 dst->role == ui::AX_ROLE_WEB_AREA) { |
| 332 dst->AddStringAttribute(ui::AX_ATTR_HTML_TAG, "#document"); |
| 333 const WebDocument& document = src.document(); |
| 334 if (name.empty()) |
| 335 name = UTF16ToUTF8(document.title()); |
| 336 dst->AddStringAttribute(ui::AX_ATTR_DOC_TITLE, |
| 337 UTF16ToUTF8(document.title())); |
| 338 dst->AddStringAttribute(ui::AX_ATTR_DOC_URL, document.url().spec()); |
| 339 dst->AddStringAttribute( |
| 340 ui::AX_ATTR_DOC_MIMETYPE, |
| 341 document.isXHTMLDocument() ? "text/xhtml" : "text/html"); |
| 342 dst->AddBoolAttribute(ui::AX_ATTR_DOC_LOADED, src.isLoaded()); |
| 343 dst->AddFloatAttribute(ui::AX_ATTR_DOC_LOADING_PROGRESS, |
| 344 src.estimatedLoadingProgress()); |
| 345 |
| 346 const WebDocumentType& doctype = document.doctype(); |
| 347 if (!doctype.isNull()) { |
| 348 dst->AddStringAttribute(ui::AX_ATTR_DOC_DOCTYPE, |
| 349 UTF16ToUTF8(doctype.name())); |
| 350 } |
| 351 |
| 352 const gfx::Size& scroll_offset = document.frame()->scrollOffset(); |
| 353 dst->AddIntAttribute(ui::AX_ATTR_SCROLL_X, scroll_offset.width()); |
| 354 dst->AddIntAttribute(ui::AX_ATTR_SCROLL_Y, scroll_offset.height()); |
| 355 |
| 356 const gfx::Size& min_offset = document.frame()->minimumScrollOffset(); |
| 357 dst->AddIntAttribute(ui::AX_ATTR_SCROLL_X_MIN, min_offset.width()); |
| 358 dst->AddIntAttribute(ui::AX_ATTR_SCROLL_Y_MIN, min_offset.height()); |
| 359 |
| 360 const gfx::Size& max_offset = document.frame()->maximumScrollOffset(); |
| 361 dst->AddIntAttribute(ui::AX_ATTR_SCROLL_X_MAX, max_offset.width()); |
| 362 dst->AddIntAttribute(ui::AX_ATTR_SCROLL_Y_MAX, max_offset.height()); |
| 363 } |
| 364 |
| 365 if (dst->role == ui::AX_ROLE_TABLE) { |
| 366 int column_count = src.columnCount(); |
| 367 int row_count = src.rowCount(); |
| 368 if (column_count > 0 && row_count > 0) { |
| 369 std::set<int32> unique_cell_id_set; |
| 370 std::vector<int32> cell_ids; |
| 371 std::vector<int32> unique_cell_ids; |
| 372 dst->AddIntAttribute(ui::AX_ATTR_TABLE_COLUMN_COUNT, column_count); |
| 373 dst->AddIntAttribute(ui::AX_ATTR_TABLE_ROW_COUNT, row_count); |
| 374 WebAXObject header = src.headerContainerObject(); |
| 375 if (!header.isDetached()) |
| 376 dst->AddIntAttribute(ui::AX_ATTR_TABLE_HEADER_ID, header.axID()); |
| 377 for (int i = 0; i < column_count * row_count; ++i) { |
| 378 WebAXObject cell = src.cellForColumnAndRow( |
| 379 i % column_count, i / column_count); |
| 380 int cell_id = -1; |
| 381 if (!cell.isDetached()) { |
| 382 cell_id = cell.axID(); |
| 383 if (unique_cell_id_set.find(cell_id) == unique_cell_id_set.end()) { |
| 384 unique_cell_id_set.insert(cell_id); |
| 385 unique_cell_ids.push_back(cell_id); |
| 386 } |
| 387 } |
| 388 cell_ids.push_back(cell_id); |
| 389 } |
| 390 dst->AddIntListAttribute(ui::AX_ATTR_CELL_IDS, cell_ids); |
| 391 dst->AddIntListAttribute(ui::AX_ATTR_UNIQUE_CELL_IDS, unique_cell_ids); |
| 392 } |
| 393 } |
| 394 |
| 395 if (dst->role == ui::AX_ROLE_ROW) { |
| 396 dst->AddIntAttribute(ui::AX_ATTR_TABLE_ROW_INDEX, src.rowIndex()); |
| 397 WebAXObject header = src.rowHeader(); |
| 398 if (!header.isDetached()) |
| 399 dst->AddIntAttribute(ui::AX_ATTR_TABLE_ROW_HEADER_ID, header.axID()); |
| 400 } |
| 401 |
| 402 if (dst->role == ui::AX_ROLE_COLUMN) { |
| 403 dst->AddIntAttribute(ui::AX_ATTR_TABLE_COLUMN_INDEX, src.columnIndex()); |
| 404 WebAXObject header = src.columnHeader(); |
| 405 if (!header.isDetached()) |
| 406 dst->AddIntAttribute(ui::AX_ATTR_TABLE_COLUMN_HEADER_ID, header.axID()); |
| 407 } |
| 408 |
| 409 if (dst->role == ui::AX_ROLE_CELL || |
| 410 dst->role == ui::AX_ROLE_ROW_HEADER || |
| 411 dst->role == ui::AX_ROLE_COLUMN_HEADER) { |
| 412 dst->AddIntAttribute(ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX, |
| 413 src.cellColumnIndex()); |
| 414 dst->AddIntAttribute(ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN, |
| 415 src.cellColumnSpan()); |
| 416 dst->AddIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_INDEX, src.cellRowIndex()); |
| 417 dst->AddIntAttribute(ui::AX_ATTR_TABLE_CELL_ROW_SPAN, src.cellRowSpan()); |
| 418 } |
| 419 |
| 420 dst->AddStringAttribute(ui::AX_ATTR_NAME, name); |
| 421 |
| 422 // Add the ids of *indirect* children - those who are children of this node, |
| 423 // but whose parent is *not* this node. One example is a table |
| 424 // cell, which is a child of both a row and a column. Because the cell's |
| 425 // parent is the row, the row adds it as a child, and the column adds it |
| 426 // as an indirect child. |
| 427 int child_count = src.childCount(); |
| 428 std::vector<int32> indirect_child_ids; |
| 429 for (int i = 0; i < child_count; ++i) { |
| 430 WebAXObject child = src.childAt(i); |
| 431 if (!is_iframe && !child.isDetached() && !IsParentUnignoredOf(src, child)) |
| 432 indirect_child_ids.push_back(child.axID()); |
| 433 } |
| 434 if (indirect_child_ids.size() > 0) { |
| 435 dst->AddIntListAttribute(ui::AX_ATTR_INDIRECT_CHILD_IDS, |
| 436 indirect_child_ids); |
| 437 } |
| 438 |
| 439 WebVector<WebAXObject> controls; |
| 440 if (src.ariaControls(controls)) |
| 441 AddIntListAttributeFromWebObjects(ui::AX_ATTR_CONTROLS_IDS, controls, dst); |
| 442 |
| 443 WebVector<WebAXObject> describedby; |
| 444 if (src.ariaDescribedby(describedby)) { |
| 445 AddIntListAttributeFromWebObjects( |
| 446 ui::AX_ATTR_DESCRIBEDBY_IDS, describedby, dst); |
| 447 } |
| 448 |
| 449 WebVector<WebAXObject> flowTo; |
| 450 if (src.ariaFlowTo(flowTo)) |
| 451 AddIntListAttributeFromWebObjects(ui::AX_ATTR_FLOWTO_IDS, flowTo, dst); |
| 452 |
| 453 WebVector<WebAXObject> labelledby; |
| 454 if (src.ariaLabelledby(labelledby)) { |
| 455 AddIntListAttributeFromWebObjects( |
| 456 ui::AX_ATTR_LABELLEDBY_IDS, labelledby, dst); |
| 457 } |
| 458 |
| 459 WebVector<WebAXObject> owns; |
| 460 if (src.ariaOwns(owns)) |
| 461 AddIntListAttributeFromWebObjects(ui::AX_ATTR_OWNS_IDS, owns, dst); |
| 462 } |
| 463 |
| 464 bool ShouldIncludeChildNode( |
| 465 const WebAXObject& parent, |
| 466 const WebAXObject& child) { |
| 467 // The child may be invalid due to issues in webkit accessibility code. |
| 468 // Don't add children that are invalid thus preventing a crash. |
| 469 // https://bugs.webkit.org/show_bug.cgi?id=44149 |
| 470 // TODO(ctguil): We may want to remove this check as webkit stabilizes. |
| 471 if (child.isDetached()) |
| 472 return false; |
| 473 |
| 474 // Skip children whose parent isn't this - see indirect_child_ids, above. |
| 475 // As an exception, include children of an iframe element. |
| 476 bool is_iframe = false; |
| 477 WebNode node = parent.node(); |
| 478 if (!node.isNull() && node.isElementNode()) { |
| 479 WebElement element = node.to<WebElement>(); |
| 480 is_iframe = (element.tagName() == base::ASCIIToUTF16("IFRAME")); |
| 481 } |
| 482 |
| 483 return (is_iframe || IsParentUnignoredOf(parent, child)); |
| 484 } |
| 485 |
| 486 } // namespace content |
OLD | NEW |