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/browser/accessibility/accessibility_tree_formatter.h"
6
7#include <oleacc.h>
8
9#include <string>
10
11#include "base/files/file_path.h"
12#include "base/strings/string_util.h"
13#include "base/strings/stringprintf.h"
14#include "base/strings/utf_string_conversions.h"
15#include "content/browser/accessibility/accessibility_tree_formatter_utils_win.h"
16#include "content/browser/accessibility/browser_accessibility_manager.h"
17#include "content/browser/accessibility/browser_accessibility_win.h"
18#include "third_party/iaccessible2/ia2_api_all.h"
19#include "ui/base/win/atl_module.h"
20
21using base::StringPrintf;
22
23namespace content {
24
25const char* ALL_ATTRIBUTES[] = {
26    "name",
27    "value",
28    "states",
29    "attributes",
30    "role_name",
31    "currentValue",
32    "minimumValue",
33    "maximumValue",
34    "description",
35    "default_action",
36    "keyboard_shortcut",
37    "location",
38    "size",
39    "index_in_parent",
40    "n_relations",
41    "group_level",
42    "similar_items_in_group",
43    "position_in_group",
44    "table_rows",
45    "table_columns",
46    "row_index",
47    "column_index",
48    "n_characters",
49    "caret_offset",
50    "n_selections",
51    "selection_start",
52    "selection_end"
53};
54
55void AccessibilityTreeFormatter::Initialize() {
56  ui::win::CreateATLModuleIfNeeded();
57}
58
59void AccessibilityTreeFormatter::AddProperties(
60    const BrowserAccessibility& node, base::DictionaryValue* dict) {
61  BrowserAccessibilityWin* acc_obj =
62      const_cast<BrowserAccessibility*>(&node)->ToBrowserAccessibilityWin();
63
64  VARIANT variant_self;
65  variant_self.vt = VT_I4;
66  variant_self.lVal = CHILDID_SELF;
67
68  dict->SetString("role", IAccessible2RoleToString(acc_obj->ia2_role()));
69
70  CComBSTR msaa_variant;
71  HRESULT hresult = acc_obj->get_accName(variant_self, &msaa_variant);
72  if (hresult == S_OK)
73    dict->SetString("name", msaa_variant.m_str);
74  hresult = acc_obj->get_accValue(variant_self, &msaa_variant);
75  if (hresult == S_OK)
76    dict->SetString("value", msaa_variant.m_str);
77
78  std::vector<base::string16> state_strings;
79  int32 ia_state = acc_obj->ia_state();
80
81  // Avoid flakiness: these states depend on whether the window is focused
82  // and the position of the mouse cursor.
83  ia_state &= ~STATE_SYSTEM_HOTTRACKED;
84  ia_state &= ~STATE_SYSTEM_OFFSCREEN;
85
86  IAccessibleStateToStringVector(ia_state, &state_strings);
87  IAccessible2StateToStringVector(acc_obj->ia2_state(), &state_strings);
88  base::ListValue* states = new base::ListValue;
89  for (std::vector<base::string16>::const_iterator it = state_strings.begin();
90       it != state_strings.end();
91       ++it) {
92    states->AppendString(base::UTF16ToUTF8(*it));
93  }
94  dict->Set("states", states);
95
96  const std::vector<base::string16>& ia2_attributes = acc_obj->ia2_attributes();
97  base::ListValue* attributes = new base::ListValue;
98  for (std::vector<base::string16>::const_iterator it = ia2_attributes.begin();
99       it != ia2_attributes.end();
100       ++it) {
101    attributes->AppendString(base::UTF16ToUTF8(*it));
102  }
103  dict->Set("attributes", attributes);
104
105  dict->SetString("role_name", acc_obj->role_name());
106
107  VARIANT currentValue;
108  if (acc_obj->get_currentValue(&currentValue) == S_OK)
109    dict->SetDouble("currentValue", V_R8(&currentValue));
110
111  VARIANT minimumValue;
112  if (acc_obj->get_minimumValue(&minimumValue) == S_OK)
113    dict->SetDouble("minimumValue", V_R8(&minimumValue));
114
115  VARIANT maximumValue;
116  if (acc_obj->get_maximumValue(&maximumValue) == S_OK)
117    dict->SetDouble("maximumValue", V_R8(&maximumValue));
118
119  hresult = acc_obj->get_accDescription(variant_self, &msaa_variant);
120  if (hresult == S_OK)
121    dict->SetString("description", msaa_variant.m_str);
122
123  hresult = acc_obj->get_accDefaultAction(variant_self, &msaa_variant);
124  if (hresult == S_OK)
125    dict->SetString("default_action", msaa_variant.m_str);
126
127  hresult = acc_obj->get_accKeyboardShortcut(variant_self, &msaa_variant);
128  if (hresult == S_OK)
129    dict->SetString("keyboard_shortcut", msaa_variant.m_str);
130
131  hresult = acc_obj->get_accHelp(variant_self, &msaa_variant);
132  if (S_OK == hresult)
133    dict->SetString("help", msaa_variant.m_str);
134
135  BrowserAccessibility* root = node.manager()->GetRoot();
136  LONG left, top, width, height;
137  LONG root_left, root_top, root_width, root_height;
138  if (acc_obj->accLocation(&left, &top, &width, &height, variant_self)
139      != S_FALSE
140      && root->ToBrowserAccessibilityWin()->accLocation(
141          &root_left, &root_top, &root_width, &root_height, variant_self)
142      != S_FALSE) {
143    base::DictionaryValue* location = new base::DictionaryValue;
144    location->SetInteger("x", left - root_left);
145    location->SetInteger("y", top - root_top);
146    dict->Set("location", location);
147
148    base::DictionaryValue* size = new base::DictionaryValue;
149    size->SetInteger("width", width);
150    size->SetInteger("height", height);
151    dict->Set("size", size);
152  }
153
154  LONG index_in_parent;
155  if (acc_obj->get_indexInParent(&index_in_parent) == S_OK)
156    dict->SetInteger("index_in_parent", index_in_parent);
157
158  LONG n_relations;
159  if (acc_obj->get_nRelations(&n_relations) == S_OK)
160    dict->SetInteger("n_relations", n_relations);
161
162  LONG group_level, similar_items_in_group, position_in_group;
163  if (acc_obj->get_groupPosition(&group_level,
164                                 &similar_items_in_group,
165                                 &position_in_group) == S_OK) {
166    dict->SetInteger("group_level", group_level);
167    dict->SetInteger("similar_items_in_group", similar_items_in_group);
168    dict->SetInteger("position_in_group", position_in_group);
169  }
170  LONG table_rows;
171  if (acc_obj->get_nRows(&table_rows) == S_OK)
172    dict->SetInteger("table_rows", table_rows);
173  LONG table_columns;
174  if (acc_obj->get_nRows(&table_columns) == S_OK)
175    dict->SetInteger("table_columns", table_columns);
176  LONG row_index;
177  if (acc_obj->get_rowIndex(&row_index) == S_OK)
178    dict->SetInteger("row_index", row_index);
179  LONG column_index;
180  if (acc_obj->get_columnIndex(&column_index) == S_OK)
181    dict->SetInteger("column_index", column_index);
182  LONG n_characters;
183  if (acc_obj->get_nCharacters(&n_characters) == S_OK)
184    dict->SetInteger("n_characters", n_characters);
185  LONG caret_offset;
186  if (acc_obj->get_caretOffset(&caret_offset) == S_OK)
187    dict->SetInteger("caret_offset", caret_offset);
188  LONG n_selections;
189  if (acc_obj->get_nSelections(&n_selections) == S_OK) {
190    dict->SetInteger("n_selections", n_selections);
191    if (n_selections > 0) {
192      LONG start, end;
193      if (acc_obj->get_selection(0, &start, &end) == S_OK) {
194        dict->SetInteger("selection_start", start);
195        dict->SetInteger("selection_end", end);
196      }
197    }
198  }
199}
200
201base::string16 AccessibilityTreeFormatter::ToString(
202    const base::DictionaryValue& dict,
203    const base::string16& indent) {
204  base::string16 line;
205
206  base::string16 role_value;
207  dict.GetString("role", &role_value);
208  WriteAttribute(true, base::UTF16ToUTF8(role_value), &line);
209
210  base::string16 name_value;
211  dict.GetString("name", &name_value);
212  WriteAttribute(true, base::StringPrintf(L"name='%ls'", name_value.c_str()),
213                 &line);
214
215  for (int i = 0; i < arraysize(ALL_ATTRIBUTES); i++) {
216    const char* attribute_name = ALL_ATTRIBUTES[i];
217    const base::Value* value;
218    if (!dict.Get(attribute_name, &value))
219      continue;
220
221    switch (value->GetType()) {
222      case base::Value::TYPE_STRING: {
223        base::string16 string_value;
224        value->GetAsString(&string_value);
225        WriteAttribute(false,
226                       StringPrintf(L"%ls='%ls'",
227                                    base::UTF8ToUTF16(attribute_name).c_str(),
228                                    string_value.c_str()),
229                       &line);
230        break;
231      }
232      case base::Value::TYPE_INTEGER: {
233        int int_value;
234        value->GetAsInteger(&int_value);
235        WriteAttribute(false,
236                       base::StringPrintf(L"%ls=%d",
237                                          base::UTF8ToUTF16(
238                                              attribute_name).c_str(),
239                                          int_value),
240                       &line);
241        break;
242      }
243      case base::Value::TYPE_DOUBLE: {
244        double double_value;
245        value->GetAsDouble(&double_value);
246        WriteAttribute(false,
247                       base::StringPrintf(L"%ls=%.2f",
248                                          base::UTF8ToUTF16(
249                                              attribute_name).c_str(),
250                                          double_value),
251                       &line);
252        break;
253      }
254      case base::Value::TYPE_LIST: {
255        // Currently all list values are string and are written without
256        // attribute names.
257        const base::ListValue* list_value;
258        value->GetAsList(&list_value);
259        for (base::ListValue::const_iterator it = list_value->begin();
260             it != list_value->end();
261             ++it) {
262          base::string16 string_value;
263          if ((*it)->GetAsString(&string_value))
264            WriteAttribute(false, string_value, &line);
265        }
266        break;
267      }
268      case base::Value::TYPE_DICTIONARY: {
269        // Currently all dictionary values are coordinates.
270        // Revisit this if that changes.
271        const base::DictionaryValue* dict_value;
272        value->GetAsDictionary(&dict_value);
273        if (strcmp(attribute_name, "size") == 0) {
274          WriteAttribute(false,
275                         FormatCoordinates("size", "width", "height",
276                                           *dict_value),
277                         &line);
278        } else if (strcmp(attribute_name, "location") == 0) {
279          WriteAttribute(false,
280                         FormatCoordinates("location", "x", "y", *dict_value),
281                         &line);
282        }
283        break;
284      }
285      default:
286        NOTREACHED();
287        break;
288    }
289  }
290
291  return indent + line + base::ASCIIToUTF16("\n");
292}
293
294// static
295const base::FilePath::StringType
296AccessibilityTreeFormatter::GetActualFileSuffix() {
297  return FILE_PATH_LITERAL("-actual-win.txt");
298}
299
300// static
301const base::FilePath::StringType
302AccessibilityTreeFormatter::GetExpectedFileSuffix() {
303  return FILE_PATH_LITERAL("-expected-win.txt");
304}
305
306// static
307const std::string AccessibilityTreeFormatter::GetAllowEmptyString() {
308  return "@WIN-ALLOW-EMPTY:";
309}
310
311// static
312const std::string AccessibilityTreeFormatter::GetAllowString() {
313  return "@WIN-ALLOW:";
314}
315
316// static
317const std::string AccessibilityTreeFormatter::GetDenyString() {
318  return "@WIN-DENY:";
319}
320
321}  // namespace content
322