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.
5 #include "content/browser/accessibility/accessibility_tree_formatter.h"
11 #include "base/files/file_path.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_piece.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/win/scoped_bstr.h"
18 #include "base/win/scoped_comptr.h"
19 #include "content/browser/accessibility/accessibility_tree_formatter_utils_win.h"
20 #include "content/browser/accessibility/browser_accessibility_manager.h"
21 #include "content/browser/accessibility/browser_accessibility_win.h"
22 #include "third_party/iaccessible2/ia2_api_all.h"
23 #include "ui/base/win/atl_module.h"
25 using base::StringPrintf
;
29 const char* ALL_ATTRIBUTES
[] = {
46 "similar_items_in_group",
59 void AccessibilityTreeFormatter::Initialize() {
60 ui::win::CreateATLModuleIfNeeded();
63 void AccessibilityTreeFormatter::AddProperties(
64 const BrowserAccessibility
& node
, base::DictionaryValue
* dict
) {
65 dict
->SetInteger("id", node
.GetId());
66 BrowserAccessibilityWin
* ax_object
=
67 const_cast<BrowserAccessibility
*>(&node
)->ToBrowserAccessibilityWin();
71 variant_self
.vt
= VT_I4
;
72 variant_self
.lVal
= CHILDID_SELF
;
74 dict
->SetString("role", IAccessible2RoleToString(ax_object
->ia2_role()));
76 base::win::ScopedBstr temp_bstr
;
77 if (SUCCEEDED(ax_object
->get_accName(variant_self
, temp_bstr
.Receive())))
78 dict
->SetString("name", base::string16(temp_bstr
, temp_bstr
.Length()));
81 if (SUCCEEDED(ax_object
->get_accValue(variant_self
, temp_bstr
.Receive())))
82 dict
->SetString("value", base::string16(temp_bstr
, temp_bstr
.Length()));
85 std::vector
<base::string16
> state_strings
;
86 int32 ia_state
= ax_object
->ia_state();
88 // Avoid flakiness: these states depend on whether the window is focused
89 // and the position of the mouse cursor.
90 ia_state
&= ~STATE_SYSTEM_HOTTRACKED
;
91 ia_state
&= ~STATE_SYSTEM_OFFSCREEN
;
93 IAccessibleStateToStringVector(ia_state
, &state_strings
);
94 IAccessible2StateToStringVector(ax_object
->ia2_state(), &state_strings
);
95 base::ListValue
* states
= new base::ListValue
;
96 for (auto it
= state_strings
.begin(); it
!= state_strings
.end(); ++it
)
97 states
->AppendString(base::UTF16ToUTF8(*it
));
98 dict
->Set("states", states
);
100 const std::vector
<base::string16
>& ia2_attributes
=
101 ax_object
->ia2_attributes();
102 base::ListValue
* attributes
= new base::ListValue
;
103 for (auto it
= ia2_attributes
.begin(); it
!= ia2_attributes
.end(); ++it
)
104 attributes
->AppendString(base::UTF16ToUTF8(*it
));
105 dict
->Set("attributes", attributes
);
107 dict
->SetString("role_name", ax_object
->role_name());
109 VARIANT currentValue
;
110 if (ax_object
->get_currentValue(¤tValue
) == S_OK
)
111 dict
->SetDouble("currentValue", V_R8(¤tValue
));
113 VARIANT minimumValue
;
114 if (ax_object
->get_minimumValue(&minimumValue
) == S_OK
)
115 dict
->SetDouble("minimumValue", V_R8(&minimumValue
));
117 VARIANT maximumValue
;
118 if (ax_object
->get_maximumValue(&maximumValue
) == S_OK
)
119 dict
->SetDouble("maximumValue", V_R8(&maximumValue
));
121 if (SUCCEEDED(ax_object
->get_accDescription(variant_self
,
122 temp_bstr
.Receive()))) {
123 dict
->SetString("description", base::string16(temp_bstr
,
124 temp_bstr
.Length()));
128 if (SUCCEEDED(ax_object
->get_accDefaultAction(variant_self
,
129 temp_bstr
.Receive()))) {
130 dict
->SetString("default_action", base::string16(temp_bstr
,
131 temp_bstr
.Length()));
136 ax_object
->get_accKeyboardShortcut(variant_self
, temp_bstr
.Receive()))) {
137 dict
->SetString("keyboard_shortcut", base::string16(temp_bstr
,
138 temp_bstr
.Length()));
142 if (SUCCEEDED(ax_object
->get_accHelp(variant_self
, temp_bstr
.Receive())))
143 dict
->SetString("help", base::string16(temp_bstr
, temp_bstr
.Length()));
146 BrowserAccessibility
* root
= node
.manager()->GetRoot();
147 LONG left
, top
, width
, height
;
148 LONG root_left
, root_top
, root_width
, root_height
;
149 if (SUCCEEDED(ax_object
->accLocation(
150 &left
, &top
, &width
, &height
, variant_self
)) &&
151 SUCCEEDED(root
->ToBrowserAccessibilityWin()->accLocation(
152 &root_left
, &root_top
, &root_width
, &root_height
, variant_self
))) {
153 base::DictionaryValue
* location
= new base::DictionaryValue
;
154 location
->SetInteger("x", left
- root_left
);
155 location
->SetInteger("y", top
- root_top
);
156 dict
->Set("location", location
);
158 base::DictionaryValue
* size
= new base::DictionaryValue
;
159 size
->SetInteger("width", width
);
160 size
->SetInteger("height", height
);
161 dict
->Set("size", size
);
164 LONG index_in_parent
;
165 if (SUCCEEDED(ax_object
->get_indexInParent(&index_in_parent
)))
166 dict
->SetInteger("index_in_parent", index_in_parent
);
169 if (SUCCEEDED(ax_object
->get_nRelations(&n_relations
)))
170 dict
->SetInteger("n_relations", n_relations
);
172 LONG group_level
, similar_items_in_group
, position_in_group
;
173 if (SUCCEEDED(ax_object
->get_groupPosition(&group_level
,
174 &similar_items_in_group
,
175 &position_in_group
))) {
176 dict
->SetInteger("group_level", group_level
);
177 dict
->SetInteger("similar_items_in_group", similar_items_in_group
);
178 dict
->SetInteger("position_in_group", position_in_group
);
182 if (SUCCEEDED(ax_object
->get_nRows(&table_rows
)))
183 dict
->SetInteger("table_rows", table_rows
);
186 if (SUCCEEDED(ax_object
->get_nRows(&table_columns
)))
187 dict
->SetInteger("table_columns", table_columns
);
190 if (SUCCEEDED(ax_object
->get_rowIndex(&row_index
)))
191 dict
->SetInteger("row_index", row_index
);
194 if (SUCCEEDED(ax_object
->get_columnIndex(&column_index
)))
195 dict
->SetInteger("column_index", column_index
);
198 if (SUCCEEDED(ax_object
->get_nCharacters(&n_characters
)))
199 dict
->SetInteger("n_characters", n_characters
);
202 if (ax_object
->get_caretOffset(&caret_offset
) == S_OK
)
203 dict
->SetInteger("caret_offset", caret_offset
);
206 if (SUCCEEDED(ax_object
->get_nSelections(&n_selections
))) {
207 dict
->SetInteger("n_selections", n_selections
);
208 if (n_selections
> 0) {
210 if (SUCCEEDED(ax_object
->get_selection(0, &start
, &end
))) {
211 dict
->SetInteger("selection_start", start
);
212 dict
->SetInteger("selection_end", end
);
218 base::string16
AccessibilityTreeFormatter::ToString(
219 const base::DictionaryValue
& dict
) {
224 dict
.GetInteger("id", &id_value
);
225 WriteAttribute(true, base::IntToString16(id_value
), &line
);
228 base::string16 role_value
;
229 dict
.GetString("role", &role_value
);
230 WriteAttribute(true, base::UTF16ToUTF8(role_value
), &line
);
232 for (int i
= 0; i
< arraysize(ALL_ATTRIBUTES
); i
++) {
233 const char* attribute_name
= ALL_ATTRIBUTES
[i
];
234 const base::Value
* value
;
235 if (!dict
.Get(attribute_name
, &value
))
238 switch (value
->GetType()) {
239 case base::Value::TYPE_STRING
: {
240 base::string16 string_value
;
241 value
->GetAsString(&string_value
);
242 WriteAttribute(false,
243 StringPrintf(L
"%ls='%ls'",
244 base::UTF8ToUTF16(attribute_name
).c_str(),
245 string_value
.c_str()),
249 case base::Value::TYPE_INTEGER
: {
251 value
->GetAsInteger(&int_value
);
252 WriteAttribute(false,
253 base::StringPrintf(L
"%ls=%d",
255 attribute_name
).c_str(),
260 case base::Value::TYPE_DOUBLE
: {
262 value
->GetAsDouble(&double_value
);
263 WriteAttribute(false,
264 base::StringPrintf(L
"%ls=%.2f",
266 attribute_name
).c_str(),
271 case base::Value::TYPE_LIST
: {
272 // Currently all list values are string and are written without
274 const base::ListValue
* list_value
;
275 value
->GetAsList(&list_value
);
276 for (base::ListValue::const_iterator it
= list_value
->begin();
277 it
!= list_value
->end();
279 base::string16 string_value
;
280 if ((*it
)->GetAsString(&string_value
))
281 WriteAttribute(false, string_value
, &line
);
285 case base::Value::TYPE_DICTIONARY
: {
286 // Currently all dictionary values are coordinates.
287 // Revisit this if that changes.
288 const base::DictionaryValue
* dict_value
;
289 value
->GetAsDictionary(&dict_value
);
290 if (strcmp(attribute_name
, "size") == 0) {
291 WriteAttribute(false,
292 FormatCoordinates("size", "width", "height",
295 } else if (strcmp(attribute_name
, "location") == 0) {
296 WriteAttribute(false,
297 FormatCoordinates("location", "x", "y", *dict_value
),
312 const base::FilePath::StringType
313 AccessibilityTreeFormatter::GetActualFileSuffix() {
314 return FILE_PATH_LITERAL("-actual-win.txt");
318 const base::FilePath::StringType
319 AccessibilityTreeFormatter::GetExpectedFileSuffix() {
320 return FILE_PATH_LITERAL("-expected-win.txt");
324 const std::string
AccessibilityTreeFormatter::GetAllowEmptyString() {
325 return "@WIN-ALLOW-EMPTY:";
329 const std::string
AccessibilityTreeFormatter::GetAllowString() {
330 return "@WIN-ALLOW:";
334 const std::string
AccessibilityTreeFormatter::GetDenyString() {
338 } // namespace content