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