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
14 using base::DoubleToString;
15 using base::IntToString;
16
17 namespace {
18
19 #ifndef NDEBUG
IntVectorToString(const std::vector<int> & items)20 std::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
33 namespace content {
34
AccessibilityNodeData()35 AccessibilityNodeData::AccessibilityNodeData()
36 : id(-1),
37 role(blink::WebAXRoleUnknown),
38 state(-1) {
39 }
40
~AccessibilityNodeData()41 AccessibilityNodeData::~AccessibilityNodeData() {
42 }
43
AddStringAttribute(StringAttribute attribute,const std::string & value)44 void AccessibilityNodeData::AddStringAttribute(
45 StringAttribute attribute, const std::string& value) {
46 string_attributes.push_back(std::make_pair(attribute, value));
47 }
48
AddIntAttribute(IntAttribute attribute,int value)49 void AccessibilityNodeData::AddIntAttribute(
50 IntAttribute attribute, int value) {
51 int_attributes.push_back(std::make_pair(attribute, value));
52 }
53
AddFloatAttribute(FloatAttribute attribute,float value)54 void AccessibilityNodeData::AddFloatAttribute(
55 FloatAttribute attribute, float value) {
56 float_attributes.push_back(std::make_pair(attribute, value));
57 }
58
AddBoolAttribute(BoolAttribute attribute,bool value)59 void AccessibilityNodeData::AddBoolAttribute(
60 BoolAttribute attribute, bool value) {
61 bool_attributes.push_back(std::make_pair(attribute, value));
62 }
63
AddIntListAttribute(IntListAttribute attribute,const std::vector<int32> & value)64 void AccessibilityNodeData::AddIntListAttribute(
65 IntListAttribute attribute, const std::vector<int32>& value) {
66 intlist_attributes.push_back(std::make_pair(attribute, value));
67 }
68
SetName(std::string name)69 void AccessibilityNodeData::SetName(std::string name) {
70 string_attributes.push_back(std::make_pair(ATTR_NAME, name));
71 }
72
SetValue(std::string value)73 void AccessibilityNodeData::SetValue(std::string value) {
74 string_attributes.push_back(std::make_pair(ATTR_VALUE, value));
75 }
76
AccessibilityNodeDataTreeNode()77 AccessibilityNodeDataTreeNode::AccessibilityNodeDataTreeNode()
78 : AccessibilityNodeData() {
79 }
80
~AccessibilityNodeDataTreeNode()81 AccessibilityNodeDataTreeNode::~AccessibilityNodeDataTreeNode() {
82 }
83
operator =(const AccessibilityNodeData & src)84 AccessibilityNodeDataTreeNode& AccessibilityNodeDataTreeNode::operator=(
85 const AccessibilityNodeData& src) {
86 AccessibilityNodeData::operator=(src);
87 return *this;
88 }
89
MakeAccessibilityNodeDataTree(const std::vector<AccessibilityNodeData> & src_vector,AccessibilityNodeDataTreeNode * dst_root)90 void MakeAccessibilityNodeDataTree(
91 const std::vector<AccessibilityNodeData>& src_vector,
92 AccessibilityNodeDataTreeNode* dst_root) {
93 // This method assumes |src_vector| contains all of the nodes of
94 // an accessibility tree, and that each parent comes before its
95 // children. Each node has an id, and the ids of its children.
96 // The output is a tree where each node contains its children.
97
98 // Initialize a hash map with all of the ids in |src_vector|.
99 base::hash_map<int32, AccessibilityNodeDataTreeNode*> id_map;
100 for (size_t i = 0; i < src_vector.size(); ++i)
101 id_map[src_vector[i].id] = NULL;
102
103 // Copy the nodes to the output tree one at a time.
104 for (size_t i = 0; i < src_vector.size(); ++i) {
105 const AccessibilityNodeData& src_node = src_vector[i];
106 AccessibilityNodeDataTreeNode* dst_node;
107
108 // If it's the first element in the vector, assume it's
109 // the root. For any other element, look for it in our
110 // hash map, and skip it if not there (meaning there was
111 // an extranous node, or the nodes were sent in the wrong
112 // order).
113 if (i == 0) {
114 dst_node = dst_root;
115 } else {
116 dst_node = id_map[src_node.id];
117 if (!dst_node)
118 continue;
119 }
120
121 // Copy the node data.
122 *dst_node = src_node;
123
124 // Add placeholders for all of the node's children in the tree,
125 // and add them to the hash map so we can find them when we
126 // encounter them in |src_vector|.
127 dst_node->children.reserve(src_node.child_ids.size());
128 for (size_t j = 0; j < src_node.child_ids.size(); ++j) {
129 int child_id = src_node.child_ids[j];
130 if (id_map.find(child_id) != id_map.end()) {
131 dst_node->children.push_back(AccessibilityNodeDataTreeNode());
132 id_map[child_id] = &dst_node->children.back();
133 }
134 }
135 }
136 }
137
138 #ifndef NDEBUG
DebugString(bool recursive) const139 std::string AccessibilityNodeData::DebugString(bool recursive) const {
140 std::string result;
141
142 result += "id=" + IntToString(id);
143
144 switch (role) {
145 case blink::WebAXRoleAlert: result += " ALERT"; break;
146 case blink::WebAXRoleAlertDialog: result += " ALERT_DIALOG"; break;
147 case blink::WebAXRoleAnnotation: result += " ANNOTATION"; break;
148 case blink::WebAXRoleApplication: result += " APPLICATION"; break;
149 case blink::WebAXRoleArticle: result += " ARTICLE"; break;
150 case blink::WebAXRoleBanner: result += " L_BANNER"; break;
151 case blink::WebAXRoleBrowser: result += " BROWSER"; break;
152 case blink::WebAXRoleBusyIndicator: result += " BUSY_INDICATOR"; break;
153 case blink::WebAXRoleButton: result += " BUTTON"; break;
154 case blink::WebAXRoleCanvas: result += " CANVAS"; break;
155 case blink::WebAXRoleCell: result += " CELL"; break;
156 case blink::WebAXRoleCheckBox: result += " CHECKBOX"; break;
157 case blink::WebAXRoleColorWell: result += " COLOR_WELL"; break;
158 case blink::WebAXRoleColumn: result += " COLUMN"; break;
159 case blink::WebAXRoleColumnHeader: result += " COLUMN_HEADER"; break;
160 case blink::WebAXRoleComboBox: result += " COMBO_BOX"; break;
161 case blink::WebAXRoleComplementary: result += " L_COMPLEMENTARY"; break;
162 case blink::WebAXRoleContentInfo: result += " L_CONTENTINFO"; break;
163 case blink::WebAXRoleDefinition: result += " DEFINITION"; break;
164 case blink::WebAXRoleDescriptionListDetail: result += " DD"; break;
165 case blink::WebAXRoleDescriptionListTerm: result += " DT"; break;
166 case blink::WebAXRoleDialog: result += " DIALOG"; break;
167 case blink::WebAXRoleDirectory: result += " DIRECTORY"; break;
168 case blink::WebAXRoleDisclosureTriangle:
169 result += " DISCLOSURE_TRIANGLE"; break;
170 case blink::WebAXRoleDiv: result += " DIV"; break;
171 case blink::WebAXRoleDocument: result += " DOCUMENT"; break;
172 case blink::WebAXRoleDrawer: result += " DRAWER"; break;
173 case blink::WebAXRoleEditableText: result += " EDITABLE_TEXT"; break;
174 case blink::WebAXRoleFooter: result += " FOOTER"; break;
175 case blink::WebAXRoleForm: result += " FORM"; break;
176 case blink::WebAXRoleGrid: result += " GRID"; break;
177 case blink::WebAXRoleGroup: result += " GROUP"; break;
178 case blink::WebAXRoleGrowArea: result += " GROW_AREA"; break;
179 case blink::WebAXRoleHeading: result += " HEADING"; break;
180 case blink::WebAXRoleHelpTag: result += " HELP_TAG"; break;
181 case blink::WebAXRoleHorizontalRule: result += " HORIZONTAL_RULE"; break;
182 case blink::WebAXRoleIgnored: result += " IGNORED"; break;
183 case blink::WebAXRoleImage: result += " IMAGE"; break;
184 case blink::WebAXRoleImageMap: result += " IMAGE_MAP"; break;
185 case blink::WebAXRoleImageMapLink: result += " IMAGE_MAP_LINK"; break;
186 case blink::WebAXRoleIncrementor: result += " INCREMENTOR"; break;
187 case blink::WebAXRoleInlineTextBox: result += " INLINE_TEXT_BOX"; break;
188 case blink::WebAXRoleLabel: result += " LABEL"; break;
189 case blink::WebAXRoleLink: result += " LINK"; break;
190 case blink::WebAXRoleList: result += " LIST"; break;
191 case blink::WebAXRoleListBox: result += " LISTBOX"; break;
192 case blink::WebAXRoleListBoxOption: result += " LISTBOX_OPTION"; break;
193 case blink::WebAXRoleListItem: result += " LIST_ITEM"; break;
194 case blink::WebAXRoleListMarker: result += " LIST_MARKER"; break;
195 case blink::WebAXRoleLog: result += " LOG"; break;
196 case blink::WebAXRoleMain: result += " L_MAIN"; break;
197 case blink::WebAXRoleMarquee: result += " MARQUEE"; break;
198 case blink::WebAXRoleMath: result += " MATH"; break;
199 case blink::WebAXRoleMatte: result += " MATTE"; break;
200 case blink::WebAXRoleMenu: result += " MENU"; break;
201 case blink::WebAXRoleMenuBar: result += " MENU_BAR"; break;
202 case blink::WebAXRoleMenuButton: result += " MENU_BUTTON"; break;
203 case blink::WebAXRoleMenuItem: result += " MENU_ITEM"; break;
204 case blink::WebAXRoleMenuListOption: result += " MENU_LIST_OPTION"; break;
205 case blink::WebAXRoleMenuListPopup: result += " MENU_LIST_POPUP"; break;
206 case blink::WebAXRoleNavigation: result += " L_NAVIGATION"; break;
207 case blink::WebAXRoleNote: result += " NOTE"; break;
208 case blink::WebAXRoleOutline: result += " OUTLINE"; break;
209 case blink::WebAXRoleParagraph: result += " PARAGRAPH"; break;
210 case blink::WebAXRolePopUpButton: result += " POPUP_BUTTON"; break;
211 case blink::WebAXRolePresentational: result += " PRESENTATIONAL"; break;
212 case blink::WebAXRoleProgressIndicator:
213 result += " PROGRESS_INDICATOR"; break;
214 case blink::WebAXRoleRadioButton: result += " RADIO_BUTTON"; break;
215 case blink::WebAXRoleRadioGroup: result += " RADIO_GROUP"; break;
216 case blink::WebAXRoleRegion: result += " REGION"; break;
217 case blink::WebAXRoleRootWebArea: result += " ROOT_WEB_AREA"; break;
218 case blink::WebAXRoleRow: result += " ROW"; break;
219 case blink::WebAXRoleRowHeader: result += " ROW_HEADER"; break;
220 case blink::WebAXRoleRuler: result += " RULER"; break;
221 case blink::WebAXRoleRulerMarker: result += " RULER_MARKER"; break;
222 case blink::WebAXRoleSVGRoot: result += " SVG_ROOT"; break;
223 case blink::WebAXRoleScrollArea: result += " SCROLLAREA"; break;
224 case blink::WebAXRoleScrollBar: result += " SCROLLBAR"; break;
225 case blink::WebAXRoleSearch: result += " L_SEARCH"; break;
226 case blink::WebAXRoleSheet: result += " SHEET"; break;
227 case blink::WebAXRoleSlider: result += " SLIDER"; break;
228 case blink::WebAXRoleSliderThumb: result += " SLIDER_THUMB"; break;
229 case blink::WebAXRoleSpinButton: result += " SPIN_BUTTON"; break;
230 case blink::WebAXRoleSpinButtonPart: result += " SPIN_BUTTON_PART"; break;
231 case blink::WebAXRoleSplitGroup: result += " SPLIT_GROUP"; break;
232 case blink::WebAXRoleSplitter: result += " SPLITTER"; break;
233 case blink::WebAXRoleStaticText: result += " STATIC_TEXT"; break;
234 case blink::WebAXRoleStatus: result += " STATUS"; break;
235 case blink::WebAXRoleSystemWide: result += " SYSTEM_WIDE"; break;
236 case blink::WebAXRoleTab: result += " TAB"; break;
237 case blink::WebAXRoleTabList: result += " TAB_LIST"; break;
238 case blink::WebAXRoleTabPanel: result += " TAB_PANEL"; break;
239 case blink::WebAXRoleTable: result += " TABLE"; break;
240 case blink::WebAXRoleTableHeaderContainer:
241 result += " TABLE_HDR_CONTAINER"; break;
242 case blink::WebAXRoleTextArea: result += " TEXTAREA"; break;
243 case blink::WebAXRoleTextField: result += " TEXT_FIELD"; break;
244 case blink::WebAXRoleTimer: result += " TIMER"; break;
245 case blink::WebAXRoleToggleButton: result += " TOGGLE_BUTTON"; break;
246 case blink::WebAXRoleToolbar: result += " TOOLBAR"; break;
247 case blink::WebAXRoleTree: result += " TREE"; break;
248 case blink::WebAXRoleTreeGrid: result += " TREE_GRID"; break;
249 case blink::WebAXRoleTreeItem: result += " TREE_ITEM"; break;
250 case blink::WebAXRoleUnknown: result += " UNKNOWN"; break;
251 case blink::WebAXRoleUserInterfaceTooltip: result += " TOOLTIP"; break;
252 case blink::WebAXRoleValueIndicator: result += " VALUE_INDICATOR"; break;
253 case blink::WebAXRoleWebArea: result += " WEB_AREA"; break;
254 case blink::WebAXRoleWindow: result += " WINDOW"; break;
255 default:
256 assert(false);
257 }
258
259 if (state & (1 << blink::WebAXStateBusy))
260 result += " BUSY";
261 if (state & (1 << blink::WebAXStateChecked))
262 result += " CHECKED";
263 if (state & (1 << blink::WebAXStateCollapsed))
264 result += " COLLAPSED";
265 if (state & (1 << blink::WebAXStateExpanded))
266 result += " EXPANDED";
267 if (state & (1 << blink::WebAXStateFocusable))
268 result += " FOCUSABLE";
269 if (state & (1 << blink::WebAXStateFocused))
270 result += " FOCUSED";
271 if (state & (1 << blink::WebAXStateHaspopup))
272 result += " HASPOPUP";
273 if (state & (1 << blink::WebAXStateHovered))
274 result += " HOTTRACKED";
275 if (state & (1 << blink::WebAXStateIndeterminate))
276 result += " INDETERMINATE";
277 if (state & (1 << blink::WebAXStateInvisible))
278 result += " INVISIBLE";
279 if (state & (1 << blink::WebAXStateLinked))
280 result += " LINKED";
281 if (state & (1 << blink::WebAXStateMultiselectable))
282 result += " MULTISELECTABLE";
283 if (state & (1 << blink::WebAXStateOffscreen))
284 result += " OFFSCREEN";
285 if (state & (1 << blink::WebAXStatePressed))
286 result += " PRESSED";
287 if (state & (1 << blink::WebAXStateProtected))
288 result += " PROTECTED";
289 if (state & (1 << blink::WebAXStateReadonly))
290 result += " READONLY";
291 if (state & (1 << blink::WebAXStateRequired))
292 result += " REQUIRED";
293 if (state & (1 << blink::WebAXStateSelectable))
294 result += " SELECTABLE";
295 if (state & (1 << blink::WebAXStateSelected))
296 result += " SELECTED";
297 if (state & (1 << blink::WebAXStateVertical))
298 result += " VERTICAL";
299 if (state & (1 << blink::WebAXStateVisited))
300 result += " VISITED";
301
302 result += " (" + IntToString(location.x()) + ", " +
303 IntToString(location.y()) + ")-(" +
304 IntToString(location.width()) + ", " +
305 IntToString(location.height()) + ")";
306
307 for (size_t i = 0; i < int_attributes.size(); ++i) {
308 std::string value = IntToString(int_attributes[i].second);
309 switch (int_attributes[i].first) {
310 case ATTR_SCROLL_X:
311 result += " scroll_x=" + value;
312 break;
313 case ATTR_SCROLL_X_MIN:
314 result += " scroll_x_min=" + value;
315 break;
316 case ATTR_SCROLL_X_MAX:
317 result += " scroll_x_max=" + value;
318 break;
319 case ATTR_SCROLL_Y:
320 result += " scroll_y=" + value;
321 break;
322 case ATTR_SCROLL_Y_MIN:
323 result += " scroll_y_min=" + value;
324 break;
325 case ATTR_SCROLL_Y_MAX:
326 result += " scroll_y_max=" + value;
327 break;
328 case ATTR_HIERARCHICAL_LEVEL:
329 result += " level=" + value;
330 break;
331 case ATTR_TEXT_SEL_START:
332 result += " sel_start=" + value;
333 break;
334 case ATTR_TEXT_SEL_END:
335 result += " sel_end=" + value;
336 break;
337 case ATTR_TABLE_ROW_COUNT:
338 result += " rows=" + value;
339 break;
340 case ATTR_TABLE_COLUMN_COUNT:
341 result += " cols=" + value;
342 break;
343 case ATTR_TABLE_CELL_COLUMN_INDEX:
344 result += " col=" + value;
345 break;
346 case ATTR_TABLE_CELL_ROW_INDEX:
347 result += " row=" + value;
348 break;
349 case ATTR_TABLE_CELL_COLUMN_SPAN:
350 result += " colspan=" + value;
351 break;
352 case ATTR_TABLE_CELL_ROW_SPAN:
353 result += " rowspan=" + value;
354 break;
355 case ATTR_TABLE_COLUMN_HEADER_ID:
356 result += " column_header_id=" + value;
357 break;
358 case ATTR_TABLE_COLUMN_INDEX:
359 result += " column_index=" + value;
360 break;
361 case ATTR_TABLE_HEADER_ID:
362 result += " header_id=" + value;
363 break;
364 case ATTR_TABLE_ROW_HEADER_ID:
365 result += " row_header_id=" + value;
366 break;
367 case ATTR_TABLE_ROW_INDEX:
368 result += " row_index=" + value;
369 break;
370 case ATTR_TITLE_UI_ELEMENT:
371 result += " title_elem=" + value;
372 break;
373 case ATTR_COLOR_VALUE_RED:
374 result += " color_value_red=" + value;
375 break;
376 case ATTR_COLOR_VALUE_GREEN:
377 result += " color_value_green=" + value;
378 break;
379 case ATTR_COLOR_VALUE_BLUE:
380 result += " color_value_blue=" + value;
381 break;
382 case ATTR_TEXT_DIRECTION:
383 switch (int_attributes[i].second) {
384 case blink::WebAXTextDirectionLR:
385 default:
386 result += " text_direction=lr";
387 break;
388 case blink::WebAXTextDirectionRL:
389 result += " text_direction=rl";
390 break;
391 case blink::WebAXTextDirectionTB:
392 result += " text_direction=tb";
393 break;
394 case blink::WebAXTextDirectionBT:
395 result += " text_direction=bt";
396 break;
397 }
398 break;
399 }
400 }
401
402 for (size_t i = 0; i < string_attributes.size(); ++i) {
403 std::string value = string_attributes[i].second;
404 switch (string_attributes[i].first) {
405 case ATTR_DOC_URL:
406 result += " doc_url=" + value;
407 break;
408 case ATTR_DOC_TITLE:
409 result += " doc_title=" + value;
410 break;
411 case ATTR_DOC_MIMETYPE:
412 result += " doc_mimetype=" + value;
413 break;
414 case ATTR_DOC_DOCTYPE:
415 result += " doc_doctype=" + value;
416 break;
417 case ATTR_ACCESS_KEY:
418 result += " access_key=" + value;
419 break;
420 case ATTR_ACTION:
421 result += " action=" + value;
422 break;
423 case ATTR_DESCRIPTION:
424 result += " description=" + value;
425 break;
426 case ATTR_DISPLAY:
427 result += " display=" + value;
428 break;
429 case ATTR_HELP:
430 result += " help=" + value;
431 break;
432 case ATTR_HTML_TAG:
433 result += " html_tag=" + value;
434 break;
435 case ATTR_LIVE_RELEVANT:
436 result += " relevant=" + value;
437 break;
438 case ATTR_LIVE_STATUS:
439 result += " live=" + value;
440 break;
441 case ATTR_CONTAINER_LIVE_RELEVANT:
442 result += " container_relevant=" + value;
443 break;
444 case ATTR_CONTAINER_LIVE_STATUS:
445 result += " container_live=" + value;
446 break;
447 case ATTR_ROLE:
448 result += " role=" + value;
449 break;
450 case ATTR_SHORTCUT:
451 result += " shortcut=" + value;
452 break;
453 case ATTR_URL:
454 result += " url=" + value;
455 break;
456 case ATTR_NAME:
457 result += " name=" + value;
458 break;
459 case ATTR_VALUE:
460 result += " value=" + value;
461 break;
462 }
463 }
464
465 for (size_t i = 0; i < float_attributes.size(); ++i) {
466 std::string value = DoubleToString(float_attributes[i].second);
467 switch (float_attributes[i].first) {
468 case ATTR_DOC_LOADING_PROGRESS:
469 result += " doc_progress=" + value;
470 break;
471 case ATTR_VALUE_FOR_RANGE:
472 result += " value_for_range=" + value;
473 break;
474 case ATTR_MAX_VALUE_FOR_RANGE:
475 result += " max_value=" + value;
476 break;
477 case ATTR_MIN_VALUE_FOR_RANGE:
478 result += " min_value=" + value;
479 break;
480 }
481 }
482
483 for (size_t i = 0; i < bool_attributes.size(); ++i) {
484 std::string value = bool_attributes[i].second ? "true" : "false";
485 switch (bool_attributes[i].first) {
486 case ATTR_DOC_LOADED:
487 result += " doc_loaded=" + value;
488 break;
489 case ATTR_BUTTON_MIXED:
490 result += " mixed=" + value;
491 break;
492 case ATTR_LIVE_ATOMIC:
493 result += " atomic=" + value;
494 break;
495 case ATTR_LIVE_BUSY:
496 result += " busy=" + value;
497 break;
498 case ATTR_CONTAINER_LIVE_ATOMIC:
499 result += " container_atomic=" + value;
500 break;
501 case ATTR_CONTAINER_LIVE_BUSY:
502 result += " container_busy=" + value;
503 break;
504 case ATTR_ARIA_READONLY:
505 result += " aria_readonly=" + value;
506 break;
507 case ATTR_CAN_SET_VALUE:
508 result += " can_set_value=" + value;
509 break;
510 case ATTR_UPDATE_LOCATION_ONLY:
511 result += " update_location_only=" + value;
512 break;
513 case ATTR_CANVAS_HAS_FALLBACK:
514 result += " has_fallback=" + value;
515 break;
516 }
517 }
518
519 for (size_t i = 0; i < intlist_attributes.size(); ++i) {
520 const std::vector<int32>& values = intlist_attributes[i].second;
521 switch (intlist_attributes[i].first) {
522 case ATTR_INDIRECT_CHILD_IDS:
523 result += " indirect_child_ids=" + IntVectorToString(values);
524 break;
525 case ATTR_LINE_BREAKS:
526 result += " line_breaks=" + IntVectorToString(values);
527 break;
528 case ATTR_CELL_IDS:
529 result += " cell_ids=" + IntVectorToString(values);
530 break;
531 case ATTR_UNIQUE_CELL_IDS:
532 result += " unique_cell_ids=" + IntVectorToString(values);
533 break;
534 case ATTR_CHARACTER_OFFSETS:
535 result += " character_offsets=" + IntVectorToString(values);
536 break;
537 case ATTR_WORD_STARTS:
538 result += " word_starts=" + IntVectorToString(values);
539 break;
540 case ATTR_WORD_ENDS:
541 result += " word_ends=" + IntVectorToString(values);
542 break;
543 }
544 }
545
546 if (!child_ids.empty())
547 result += " child_ids=" + IntVectorToString(child_ids);
548
549 return result;
550 }
551
DebugString(bool recursive) const552 std::string AccessibilityNodeDataTreeNode::DebugString(bool recursive) const {
553 std::string result;
554
555 static int indent = 0;
556 result += "\n";
557 for (int i = 0; i < indent; ++i)
558 result += " ";
559
560 result += AccessibilityNodeData::DebugString(recursive);
561
562 if (recursive) {
563 result += "\n";
564 ++indent;
565 for (size_t i = 0; i < children.size(); ++i)
566 result += children[i].DebugString(true);
567 --indent;
568 }
569
570 return result;
571 }
572
573 #endif // ifndef NDEBUG
574
575 } // namespace content
576