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/common/accessibility_node_data.h" 6 7#include <set> 8 9#include "base/containers/hash_tables.h" 10#include "base/strings/string_number_conversions.h" 11#include "base/strings/string_util.h" 12#include "base/strings/utf_string_conversions.h" 13 14using base::DoubleToString; 15using base::IntToString; 16 17namespace { 18 19#ifndef NDEBUG 20std::string IntVectorToString(const std::vector<int>& items) { 21 std::string str; 22 for (size_t i = 0; i < items.size(); ++i) { 23 if (i > 0) 24 str += ","; 25 str += IntToString(items[i]); 26 } 27 return str; 28} 29#endif 30 31} // Anonymous namespace 32 33namespace content { 34 35AccessibilityNodeData::AccessibilityNodeData() 36 : id(-1), 37 role(ROLE_UNKNOWN), 38 state(-1) { 39} 40 41AccessibilityNodeData::~AccessibilityNodeData() { 42} 43 44AccessibilityNodeDataTreeNode::AccessibilityNodeDataTreeNode() 45 : AccessibilityNodeData() { 46} 47 48AccessibilityNodeDataTreeNode::~AccessibilityNodeDataTreeNode() { 49} 50 51AccessibilityNodeDataTreeNode& AccessibilityNodeDataTreeNode::operator=( 52 const AccessibilityNodeData& src) { 53 AccessibilityNodeData::operator=(src); 54 return *this; 55} 56 57void MakeAccessibilityNodeDataTree( 58 const std::vector<AccessibilityNodeData>& src_vector, 59 AccessibilityNodeDataTreeNode* dst_root) { 60 // This method assumes |src_vector| contains all of the nodes of 61 // an accessibility tree, and that each parent comes before its 62 // children. Each node has an id, and the ids of its children. 63 // The output is a tree where each node contains its children. 64 65 // Initialize a hash map with all of the ids in |src_vector|. 66 base::hash_map<int32, AccessibilityNodeDataTreeNode*> id_map; 67 for (size_t i = 0; i < src_vector.size(); ++i) 68 id_map[src_vector[i].id] = NULL; 69 70 // Copy the nodes to the output tree one at a time. 71 for (size_t i = 0; i < src_vector.size(); ++i) { 72 const AccessibilityNodeData& src_node = src_vector[i]; 73 AccessibilityNodeDataTreeNode* dst_node; 74 75 // If it's the first element in the vector, assume it's 76 // the root. For any other element, look for it in our 77 // hash map, and skip it if not there (meaning there was 78 // an extranous node, or the nodes were sent in the wrong 79 // order). 80 if (i == 0) { 81 dst_node = dst_root; 82 } else { 83 dst_node = id_map[src_node.id]; 84 if (!dst_node) 85 continue; 86 } 87 88 // Copy the node data. 89 *dst_node = src_node; 90 91 // Add placeholders for all of the node's children in the tree, 92 // and add them to the hash map so we can find them when we 93 // encounter them in |src_vector|. 94 dst_node->children.reserve(src_node.child_ids.size()); 95 for (size_t j = 0; j < src_node.child_ids.size(); ++j) { 96 int child_id = src_node.child_ids[j]; 97 if (id_map.find(child_id) != id_map.end()) { 98 dst_node->children.push_back(AccessibilityNodeDataTreeNode()); 99 id_map[child_id] = &dst_node->children.back(); 100 } 101 } 102 } 103} 104 105#ifndef NDEBUG 106std::string AccessibilityNodeData::DebugString(bool recursive) const { 107 std::string result; 108 109 result += "id=" + IntToString(id); 110 111 switch (role) { 112 case ROLE_ALERT: result += " ALERT"; break; 113 case ROLE_ALERT_DIALOG: result += " ALERT_DIALOG"; break; 114 case ROLE_ANNOTATION: result += " ANNOTATION"; break; 115 case ROLE_APPLICATION: result += " APPLICATION"; break; 116 case ROLE_ARTICLE: result += " ARTICLE"; break; 117 case ROLE_BROWSER: result += " BROWSER"; break; 118 case ROLE_BUSY_INDICATOR: result += " BUSY_INDICATOR"; break; 119 case ROLE_BUTTON: result += " BUTTON"; break; 120 case ROLE_CANVAS: result += " CANVAS"; break; 121 case ROLE_CANVAS_WITH_FALLBACK_CONTENT: result += " CANVAS_FALLBACK"; break; 122 case ROLE_CELL: result += " CELL"; break; 123 case ROLE_CHECKBOX: result += " CHECKBOX"; break; 124 case ROLE_COLOR_WELL: result += " COLOR_WELL"; break; 125 case ROLE_COLUMN: result += " COLUMN"; break; 126 case ROLE_COLUMN_HEADER: result += " COLUMN_HEADER"; break; 127 case ROLE_COMBO_BOX: result += " COMBO_BOX"; break; 128 case ROLE_DEFINITION: result += " DEFINITION"; break; 129 case ROLE_DESCRIPTION_LIST_DETAIL: result += " DD"; break; 130 case ROLE_DESCRIPTION_LIST_TERM: result += " DT"; break; 131 case ROLE_DIALOG: result += " DIALOG"; break; 132 case ROLE_DIRECTORY: result += " DIRECTORY"; break; 133 case ROLE_DISCLOSURE_TRIANGLE: result += " DISCLOSURE_TRIANGLE"; break; 134 case ROLE_DIV: result += " DIV"; break; 135 case ROLE_DOCUMENT: result += " DOCUMENT"; break; 136 case ROLE_DRAWER: result += " DRAWER"; break; 137 case ROLE_EDITABLE_TEXT: result += " EDITABLE_TEXT"; break; 138 case ROLE_FOOTER: result += " FOOTER"; break; 139 case ROLE_FORM: result += " FORM"; break; 140 case ROLE_GRID: result += " GRID"; break; 141 case ROLE_GROUP: result += " GROUP"; break; 142 case ROLE_GROW_AREA: result += " GROW_AREA"; break; 143 case ROLE_HEADING: result += " HEADING"; break; 144 case ROLE_HELP_TAG: result += " HELP_TAG"; break; 145 case ROLE_HORIZONTAL_RULE: result += " HORIZONTAL_RULE"; break; 146 case ROLE_IGNORED: result += " IGNORED"; break; 147 case ROLE_IMAGE: result += " IMAGE"; break; 148 case ROLE_IMAGE_MAP: result += " IMAGE_MAP"; break; 149 case ROLE_IMAGE_MAP_LINK: result += " IMAGE_MAP_LINK"; break; 150 case ROLE_INCREMENTOR: result += " INCREMENTOR"; break; 151 case ROLE_LABEL: result += " LABEL"; break; 152 case ROLE_LANDMARK_APPLICATION: result += " L_APPLICATION"; break; 153 case ROLE_LANDMARK_BANNER: result += " L_BANNER"; break; 154 case ROLE_LANDMARK_COMPLEMENTARY: result += " L_COMPLEMENTARY"; break; 155 case ROLE_LANDMARK_CONTENTINFO: result += " L_CONTENTINFO"; break; 156 case ROLE_LANDMARK_MAIN: result += " L_MAIN"; break; 157 case ROLE_LANDMARK_NAVIGATION: result += " L_NAVIGATION"; break; 158 case ROLE_LANDMARK_SEARCH: result += " L_SEARCH"; break; 159 case ROLE_LINK: result += " LINK"; break; 160 case ROLE_LIST: result += " LIST"; break; 161 case ROLE_LISTBOX: result += " LISTBOX"; break; 162 case ROLE_LISTBOX_OPTION: result += " LISTBOX_OPTION"; break; 163 case ROLE_LIST_ITEM: result += " LIST_ITEM"; break; 164 case ROLE_LIST_MARKER: result += " LIST_MARKER"; break; 165 case ROLE_LOG: result += " LOG"; break; 166 case ROLE_MARQUEE: result += " MARQUEE"; break; 167 case ROLE_MATH: result += " MATH"; break; 168 case ROLE_MATTE: result += " MATTE"; break; 169 case ROLE_MENU: result += " MENU"; break; 170 case ROLE_MENU_BAR: result += " MENU_BAR"; break; 171 case ROLE_MENU_BUTTON: result += " MENU_BUTTON"; break; 172 case ROLE_MENU_ITEM: result += " MENU_ITEM"; break; 173 case ROLE_MENU_LIST_OPTION: result += " MENU_LIST_OPTION"; break; 174 case ROLE_MENU_LIST_POPUP: result += " MENU_LIST_POPUP"; break; 175 case ROLE_NOTE: result += " NOTE"; break; 176 case ROLE_OUTLINE: result += " OUTLINE"; break; 177 case ROLE_PARAGRAPH: result += " PARAGRAPH"; break; 178 case ROLE_POPUP_BUTTON: result += " POPUP_BUTTON"; break; 179 case ROLE_PRESENTATIONAL: result += " PRESENTATIONAL"; break; 180 case ROLE_PROGRESS_INDICATOR: result += " PROGRESS_INDICATOR"; break; 181 case ROLE_RADIO_BUTTON: result += " RADIO_BUTTON"; break; 182 case ROLE_RADIO_GROUP: result += " RADIO_GROUP"; break; 183 case ROLE_REGION: result += " REGION"; break; 184 case ROLE_ROOT_WEB_AREA: result += " ROOT_WEB_AREA"; break; 185 case ROLE_ROW: result += " ROW"; break; 186 case ROLE_ROW_HEADER: result += " ROW_HEADER"; break; 187 case ROLE_RULER: result += " RULER"; break; 188 case ROLE_RULER_MARKER: result += " RULER_MARKER"; break; 189 case ROLE_SCROLLAREA: result += " SCROLLAREA"; break; 190 case ROLE_SCROLLBAR: result += " SCROLLBAR"; break; 191 case ROLE_SHEET: result += " SHEET"; break; 192 case ROLE_SLIDER: result += " SLIDER"; break; 193 case ROLE_SLIDER_THUMB: result += " SLIDER_THUMB"; break; 194 case ROLE_SPIN_BUTTON: result += " SPIN_BUTTON"; break; 195 case ROLE_SPIN_BUTTON_PART: result += " SPIN_BUTTON_PART"; break; 196 case ROLE_SPLITTER: result += " SPLITTER"; break; 197 case ROLE_SPLIT_GROUP: result += " SPLIT_GROUP"; break; 198 case ROLE_STATIC_TEXT: result += " STATIC_TEXT"; break; 199 case ROLE_STATUS: result += " STATUS"; break; 200 case ROLE_SVG_ROOT: result += " SVG_ROOT"; break; 201 case ROLE_SYSTEM_WIDE: result += " SYSTEM_WIDE"; break; 202 case ROLE_TAB: result += " TAB"; break; 203 case ROLE_TABLE: result += " TABLE"; break; 204 case ROLE_TABLE_HEADER_CONTAINER: result += " TABLE_HDR_CONTAINER"; break; 205 case ROLE_TAB_GROUP_UNUSED: result += " TAB_GROUP_UNUSED"; break; 206 case ROLE_TAB_LIST: result += " TAB_LIST"; break; 207 case ROLE_TAB_PANEL: result += " TAB_PANEL"; break; 208 case ROLE_TEXTAREA: result += " TEXTAREA"; break; 209 case ROLE_TEXT_FIELD: result += " TEXT_FIELD"; break; 210 case ROLE_TIMER: result += " TIMER"; break; 211 case ROLE_TOGGLE_BUTTON: result += " TOGGLE_BUTTON"; break; 212 case ROLE_TOOLBAR: result += " TOOLBAR"; break; 213 case ROLE_TOOLTIP: result += " TOOLTIP"; break; 214 case ROLE_TREE: result += " TREE"; break; 215 case ROLE_TREE_GRID: result += " TREE_GRID"; break; 216 case ROLE_TREE_ITEM: result += " TREE_ITEM"; break; 217 case ROLE_UNKNOWN: result += " UNKNOWN"; break; 218 case ROLE_VALUE_INDICATOR: result += " VALUE_INDICATOR"; break; 219 case ROLE_WEBCORE_LINK: result += " WEBCORE_LINK"; break; 220 case ROLE_WEB_AREA: result += " WEB_AREA"; break; 221 case ROLE_WINDOW: result += " WINDOW"; break; 222 default: 223 assert(false); 224 } 225 226 if (state & (1 << STATE_BUSY)) 227 result += " BUSY"; 228 if (state & (1 << STATE_CHECKED)) 229 result += " CHECKED"; 230 if (state & (1 << STATE_COLLAPSED)) 231 result += " COLLAPSED"; 232 if (state & (1 << STATE_EXPANDED)) 233 result += " EXPANDED"; 234 if (state & (1 << STATE_FOCUSABLE)) 235 result += " FOCUSABLE"; 236 if (state & (1 << STATE_FOCUSED)) 237 result += " FOCUSED"; 238 if (state & (1 << STATE_HASPOPUP)) 239 result += " HASPOPUP"; 240 if (state & (1 << STATE_HOTTRACKED)) 241 result += " HOTTRACKED"; 242 if (state & (1 << STATE_INDETERMINATE)) 243 result += " INDETERMINATE"; 244 if (state & (1 << STATE_INVISIBLE)) 245 result += " INVISIBLE"; 246 if (state & (1 << STATE_LINKED)) 247 result += " LINKED"; 248 if (state & (1 << STATE_MULTISELECTABLE)) 249 result += " MULTISELECTABLE"; 250 if (state & (1 << STATE_OFFSCREEN)) 251 result += " OFFSCREEN"; 252 if (state & (1 << STATE_PRESSED)) 253 result += " PRESSED"; 254 if (state & (1 << STATE_PROTECTED)) 255 result += " PROTECTED"; 256 if (state & (1 << STATE_READONLY)) 257 result += " READONLY"; 258 if (state & (1 << STATE_REQUIRED)) 259 result += " REQUIRED"; 260 if (state & (1 << STATE_SELECTABLE)) 261 result += " SELECTABLE"; 262 if (state & (1 << STATE_SELECTED)) 263 result += " SELECTED"; 264 if (state & (1 << STATE_TRAVERSED)) 265 result += " TRAVERSED"; 266 if (state & (1 << STATE_UNAVAILABLE)) 267 result += " UNAVAILABLE"; 268 if (state & (1 << STATE_VERTICAL)) 269 result += " VERTICAL"; 270 if (state & (1 << STATE_VISITED)) 271 result += " VISITED"; 272 273 std::string tmp = UTF16ToUTF8(name); 274 RemoveChars(tmp, "\n", &tmp); 275 if (!tmp.empty()) 276 result += " name=" + tmp; 277 278 tmp = UTF16ToUTF8(value); 279 RemoveChars(tmp, "\n", &tmp); 280 if (!tmp.empty()) 281 result += " value=" + tmp; 282 283 result += " (" + IntToString(location.x()) + ", " + 284 IntToString(location.y()) + ")-(" + 285 IntToString(location.width()) + ", " + 286 IntToString(location.height()) + ")"; 287 288 for (std::map<IntAttribute, int32>::const_iterator iter = 289 int_attributes.begin(); 290 iter != int_attributes.end(); 291 ++iter) { 292 std::string value = IntToString(iter->second); 293 switch (iter->first) { 294 case ATTR_SCROLL_X: 295 result += " scroll_x=" + value; 296 break; 297 case ATTR_SCROLL_X_MIN: 298 result += " scroll_x_min=" + value; 299 break; 300 case ATTR_SCROLL_X_MAX: 301 result += " scroll_x_max=" + value; 302 break; 303 case ATTR_SCROLL_Y: 304 result += " scroll_y=" + value; 305 break; 306 case ATTR_SCROLL_Y_MIN: 307 result += " scroll_y_min=" + value; 308 break; 309 case ATTR_SCROLL_Y_MAX: 310 result += " scroll_y_max=" + value; 311 break; 312 case ATTR_HIERARCHICAL_LEVEL: 313 result += " level=" + value; 314 break; 315 case ATTR_TEXT_SEL_START: 316 result += " sel_start=" + value; 317 break; 318 case ATTR_TEXT_SEL_END: 319 result += " sel_end=" + value; 320 break; 321 case ATTR_TABLE_ROW_COUNT: 322 result += " rows=" + value; 323 break; 324 case ATTR_TABLE_COLUMN_COUNT: 325 result += " cols=" + value; 326 break; 327 case ATTR_TABLE_CELL_COLUMN_INDEX: 328 result += " col=" + value; 329 break; 330 case ATTR_TABLE_CELL_ROW_INDEX: 331 result += " row=" + value; 332 break; 333 case ATTR_TABLE_CELL_COLUMN_SPAN: 334 result += " colspan=" + value; 335 break; 336 case ATTR_TABLE_CELL_ROW_SPAN: 337 result += " rowspan=" + value; 338 break; 339 case ATTR_TABLE_COLUMN_HEADER_ID: 340 result += " column_header_id=" + value; 341 break; 342 case ATTR_TABLE_COLUMN_INDEX: 343 result += " column_index=" + value; 344 break; 345 case ATTR_TABLE_HEADER_ID: 346 result += " header_id=" + value; 347 break; 348 case ATTR_TABLE_ROW_HEADER_ID: 349 result += " row_header_id=" + value; 350 break; 351 case ATTR_TABLE_ROW_INDEX: 352 result += " row_index=" + value; 353 break; 354 case ATTR_TITLE_UI_ELEMENT: 355 result += " title_elem=" + value; 356 break; 357 case ATTR_COLOR_VALUE_RED: 358 result += " color_value_red=" + value; 359 break; 360 case ATTR_COLOR_VALUE_GREEN: 361 result += " color_value_green=" + value; 362 break; 363 case ATTR_COLOR_VALUE_BLUE: 364 result += " color_value_blue=" + value; 365 break; 366 } 367 } 368 369 for (std::map<StringAttribute, string16>::const_iterator iter = 370 string_attributes.begin(); 371 iter != string_attributes.end(); 372 ++iter) { 373 std::string value = UTF16ToUTF8(iter->second); 374 switch (iter->first) { 375 case ATTR_DOC_URL: 376 result += " doc_url=" + value; 377 break; 378 case ATTR_DOC_TITLE: 379 result += " doc_title=" + value; 380 break; 381 case ATTR_DOC_MIMETYPE: 382 result += " doc_mimetype=" + value; 383 break; 384 case ATTR_DOC_DOCTYPE: 385 result += " doc_doctype=" + value; 386 break; 387 case ATTR_ACCESS_KEY: 388 result += " access_key=" + value; 389 break; 390 case ATTR_ACTION: 391 result += " action=" + value; 392 break; 393 case ATTR_DESCRIPTION: 394 result += " description=" + value; 395 break; 396 case ATTR_DISPLAY: 397 result += " display=" + value; 398 break; 399 case ATTR_HELP: 400 result += " help=" + value; 401 break; 402 case ATTR_HTML_TAG: 403 result += " html_tag=" + value; 404 break; 405 case ATTR_LIVE_RELEVANT: 406 result += " relevant=" + value; 407 break; 408 case ATTR_LIVE_STATUS: 409 result += " live=" + value; 410 break; 411 case ATTR_CONTAINER_LIVE_RELEVANT: 412 result += " container_relevant=" + value; 413 break; 414 case ATTR_CONTAINER_LIVE_STATUS: 415 result += " container_live=" + value; 416 break; 417 case ATTR_ROLE: 418 result += " role=" + value; 419 break; 420 case ATTR_SHORTCUT: 421 result += " shortcut=" + value; 422 break; 423 case ATTR_URL: 424 result += " url=" + value; 425 break; 426 } 427 } 428 429 for (std::map<FloatAttribute, float>::const_iterator iter = 430 float_attributes.begin(); 431 iter != float_attributes.end(); 432 ++iter) { 433 std::string value = DoubleToString(iter->second); 434 switch (iter->first) { 435 case ATTR_DOC_LOADING_PROGRESS: 436 result += " doc_progress=" + value; 437 break; 438 case ATTR_VALUE_FOR_RANGE: 439 result += " value_for_range=" + value; 440 break; 441 case ATTR_MAX_VALUE_FOR_RANGE: 442 result += " max_value=" + value; 443 break; 444 case ATTR_MIN_VALUE_FOR_RANGE: 445 result += " min_value=" + value; 446 break; 447 } 448 } 449 450 for (std::map<BoolAttribute, bool>::const_iterator iter = 451 bool_attributes.begin(); 452 iter != bool_attributes.end(); 453 ++iter) { 454 std::string value = iter->second ? "true" : "false"; 455 switch (iter->first) { 456 case ATTR_DOC_LOADED: 457 result += " doc_loaded=" + value; 458 break; 459 case ATTR_BUTTON_MIXED: 460 result += " mixed=" + value; 461 break; 462 case ATTR_LIVE_ATOMIC: 463 result += " atomic=" + value; 464 break; 465 case ATTR_LIVE_BUSY: 466 result += " busy=" + value; 467 break; 468 case ATTR_CONTAINER_LIVE_ATOMIC: 469 result += " container_atomic=" + value; 470 break; 471 case ATTR_CONTAINER_LIVE_BUSY: 472 result += " container_busy=" + value; 473 break; 474 case ATTR_ARIA_READONLY: 475 result += " aria_readonly=" + value; 476 break; 477 case ATTR_CAN_SET_VALUE: 478 result += " can_set_value=" + value; 479 break; 480 case ATTR_UPDATE_LOCATION_ONLY: 481 result += " update_location_only=" + value; 482 break; 483 } 484 } 485 486 if (!child_ids.empty()) 487 result += " child_ids=" + IntVectorToString(child_ids); 488 489 if (!indirect_child_ids.empty()) 490 result += " indirect_child_ids=" + IntVectorToString(indirect_child_ids); 491 492 if (!line_breaks.empty()) 493 result += " line_breaks=" + IntVectorToString(line_breaks); 494 495 if (!cell_ids.empty()) 496 result += " cell_ids=" + IntVectorToString(cell_ids); 497 498 return result; 499} 500 501std::string AccessibilityNodeDataTreeNode::DebugString(bool recursive) const { 502 std::string result; 503 504 static int indent = 0; 505 result += "\n"; 506 for (int i = 0; i < indent; ++i) 507 result += " "; 508 509 result += AccessibilityNodeData::DebugString(recursive); 510 511 if (recursive) { 512 result += "\n"; 513 ++indent; 514 for (size_t i = 0; i < children.size(); ++i) 515 result += children[i].DebugString(true); 516 --indent; 517 } 518 519 return result; 520} 521 522#endif // ifndef NDEBUG 523 524} // namespace content 525