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_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "content/browser/accessibility/accessibility_tree_formatter_utils_win.h"
17 #include "content/browser/accessibility/browser_accessibility_manager.h"
18 #include "content/browser/accessibility/browser_accessibility_win.h"
19 #include "third_party/iaccessible2/ia2_api_all.h"
20 #include "ui/base/win/atl_module.h"
22 using base::StringPrintf
;
26 const char* ALL_ATTRIBUTES
[] = {
43 "similar_items_in_group",
56 void AccessibilityTreeFormatter::Initialize() {
57 ui::win::CreateATLModuleIfNeeded();
60 void AccessibilityTreeFormatter::AddProperties(
61 const BrowserAccessibility
& node
, base::DictionaryValue
* dict
) {
62 dict
->SetInteger("id", node
.GetId());
63 BrowserAccessibilityWin
* acc_obj
=
64 const_cast<BrowserAccessibility
*>(&node
)->ToBrowserAccessibilityWin();
67 variant_self
.vt
= VT_I4
;
68 variant_self
.lVal
= CHILDID_SELF
;
70 dict
->SetString("role", IAccessible2RoleToString(acc_obj
->ia2_role()));
72 CComBSTR msaa_variant
;
73 HRESULT hresult
= acc_obj
->get_accName(variant_self
, &msaa_variant
);
75 dict
->SetString("name", msaa_variant
.m_str
);
76 hresult
= acc_obj
->get_accValue(variant_self
, &msaa_variant
);
78 dict
->SetString("value", msaa_variant
.m_str
);
80 std::vector
<base::string16
> state_strings
;
81 int32 ia_state
= acc_obj
->ia_state();
83 // Avoid flakiness: these states depend on whether the window is focused
84 // and the position of the mouse cursor.
85 ia_state
&= ~STATE_SYSTEM_HOTTRACKED
;
86 ia_state
&= ~STATE_SYSTEM_OFFSCREEN
;
88 IAccessibleStateToStringVector(ia_state
, &state_strings
);
89 IAccessible2StateToStringVector(acc_obj
->ia2_state(), &state_strings
);
90 base::ListValue
* states
= new base::ListValue
;
91 for (std::vector
<base::string16
>::const_iterator it
= state_strings
.begin();
92 it
!= state_strings
.end();
94 states
->AppendString(base::UTF16ToUTF8(*it
));
96 dict
->Set("states", states
);
98 const std::vector
<base::string16
>& ia2_attributes
= acc_obj
->ia2_attributes();
99 base::ListValue
* attributes
= new base::ListValue
;
100 for (std::vector
<base::string16
>::const_iterator it
= ia2_attributes
.begin();
101 it
!= ia2_attributes
.end();
103 attributes
->AppendString(base::UTF16ToUTF8(*it
));
105 dict
->Set("attributes", attributes
);
107 dict
->SetString("role_name", acc_obj
->role_name());
109 VARIANT currentValue
;
110 if (acc_obj
->get_currentValue(¤tValue
) == S_OK
)
111 dict
->SetDouble("currentValue", V_R8(¤tValue
));
113 VARIANT minimumValue
;
114 if (acc_obj
->get_minimumValue(&minimumValue
) == S_OK
)
115 dict
->SetDouble("minimumValue", V_R8(&minimumValue
));
117 VARIANT maximumValue
;
118 if (acc_obj
->get_maximumValue(&maximumValue
) == S_OK
)
119 dict
->SetDouble("maximumValue", V_R8(&maximumValue
));
121 hresult
= acc_obj
->get_accDescription(variant_self
, &msaa_variant
);
123 dict
->SetString("description", msaa_variant
.m_str
);
125 hresult
= acc_obj
->get_accDefaultAction(variant_self
, &msaa_variant
);
127 dict
->SetString("default_action", msaa_variant
.m_str
);
129 hresult
= acc_obj
->get_accKeyboardShortcut(variant_self
, &msaa_variant
);
131 dict
->SetString("keyboard_shortcut", msaa_variant
.m_str
);
133 hresult
= acc_obj
->get_accHelp(variant_self
, &msaa_variant
);
135 dict
->SetString("help", msaa_variant
.m_str
);
137 BrowserAccessibility
* root
= node
.manager()->GetRoot();
138 LONG left
, top
, width
, height
;
139 LONG root_left
, root_top
, root_width
, root_height
;
140 if (acc_obj
->accLocation(&left
, &top
, &width
, &height
, variant_self
)
142 && root
->ToBrowserAccessibilityWin()->accLocation(
143 &root_left
, &root_top
, &root_width
, &root_height
, variant_self
)
145 base::DictionaryValue
* location
= new base::DictionaryValue
;
146 location
->SetInteger("x", left
- root_left
);
147 location
->SetInteger("y", top
- root_top
);
148 dict
->Set("location", location
);
150 base::DictionaryValue
* size
= new base::DictionaryValue
;
151 size
->SetInteger("width", width
);
152 size
->SetInteger("height", height
);
153 dict
->Set("size", size
);
156 LONG index_in_parent
;
157 if (acc_obj
->get_indexInParent(&index_in_parent
) == S_OK
)
158 dict
->SetInteger("index_in_parent", index_in_parent
);
161 if (acc_obj
->get_nRelations(&n_relations
) == S_OK
)
162 dict
->SetInteger("n_relations", n_relations
);
164 LONG group_level
, similar_items_in_group
, position_in_group
;
165 if (acc_obj
->get_groupPosition(&group_level
,
166 &similar_items_in_group
,
167 &position_in_group
) == S_OK
) {
168 dict
->SetInteger("group_level", group_level
);
169 dict
->SetInteger("similar_items_in_group", similar_items_in_group
);
170 dict
->SetInteger("position_in_group", position_in_group
);
173 if (acc_obj
->get_nRows(&table_rows
) == S_OK
)
174 dict
->SetInteger("table_rows", table_rows
);
176 if (acc_obj
->get_nRows(&table_columns
) == S_OK
)
177 dict
->SetInteger("table_columns", table_columns
);
179 if (acc_obj
->get_rowIndex(&row_index
) == S_OK
)
180 dict
->SetInteger("row_index", row_index
);
182 if (acc_obj
->get_columnIndex(&column_index
) == S_OK
)
183 dict
->SetInteger("column_index", column_index
);
185 if (acc_obj
->get_nCharacters(&n_characters
) == S_OK
)
186 dict
->SetInteger("n_characters", n_characters
);
188 if (acc_obj
->get_caretOffset(&caret_offset
) == S_OK
)
189 dict
->SetInteger("caret_offset", caret_offset
);
191 if (acc_obj
->get_nSelections(&n_selections
) == S_OK
) {
192 dict
->SetInteger("n_selections", n_selections
);
193 if (n_selections
> 0) {
195 if (acc_obj
->get_selection(0, &start
, &end
) == S_OK
) {
196 dict
->SetInteger("selection_start", start
);
197 dict
->SetInteger("selection_end", end
);
203 base::string16
AccessibilityTreeFormatter::ToString(
204 const base::DictionaryValue
& dict
) {
209 dict
.GetInteger("id", &id_value
);
210 WriteAttribute(true, base::IntToString16(id_value
), &line
);
213 base::string16 role_value
;
214 dict
.GetString("role", &role_value
);
215 WriteAttribute(true, base::UTF16ToUTF8(role_value
), &line
);
217 for (int i
= 0; i
< arraysize(ALL_ATTRIBUTES
); i
++) {
218 const char* attribute_name
= ALL_ATTRIBUTES
[i
];
219 const base::Value
* value
;
220 if (!dict
.Get(attribute_name
, &value
))
223 switch (value
->GetType()) {
224 case base::Value::TYPE_STRING
: {
225 base::string16 string_value
;
226 value
->GetAsString(&string_value
);
227 WriteAttribute(false,
228 StringPrintf(L
"%ls='%ls'",
229 base::UTF8ToUTF16(attribute_name
).c_str(),
230 string_value
.c_str()),
234 case base::Value::TYPE_INTEGER
: {
236 value
->GetAsInteger(&int_value
);
237 WriteAttribute(false,
238 base::StringPrintf(L
"%ls=%d",
240 attribute_name
).c_str(),
245 case base::Value::TYPE_DOUBLE
: {
247 value
->GetAsDouble(&double_value
);
248 WriteAttribute(false,
249 base::StringPrintf(L
"%ls=%.2f",
251 attribute_name
).c_str(),
256 case base::Value::TYPE_LIST
: {
257 // Currently all list values are string and are written without
259 const base::ListValue
* list_value
;
260 value
->GetAsList(&list_value
);
261 for (base::ListValue::const_iterator it
= list_value
->begin();
262 it
!= list_value
->end();
264 base::string16 string_value
;
265 if ((*it
)->GetAsString(&string_value
))
266 WriteAttribute(false, string_value
, &line
);
270 case base::Value::TYPE_DICTIONARY
: {
271 // Currently all dictionary values are coordinates.
272 // Revisit this if that changes.
273 const base::DictionaryValue
* dict_value
;
274 value
->GetAsDictionary(&dict_value
);
275 if (strcmp(attribute_name
, "size") == 0) {
276 WriteAttribute(false,
277 FormatCoordinates("size", "width", "height",
280 } else if (strcmp(attribute_name
, "location") == 0) {
281 WriteAttribute(false,
282 FormatCoordinates("location", "x", "y", *dict_value
),
297 const base::FilePath::StringType
298 AccessibilityTreeFormatter::GetActualFileSuffix() {
299 return FILE_PATH_LITERAL("-actual-win.txt");
303 const base::FilePath::StringType
304 AccessibilityTreeFormatter::GetExpectedFileSuffix() {
305 return FILE_PATH_LITERAL("-expected-win.txt");
309 const std::string
AccessibilityTreeFormatter::GetAllowEmptyString() {
310 return "@WIN-ALLOW-EMPTY:";
314 const std::string
AccessibilityTreeFormatter::GetAllowString() {
315 return "@WIN-ALLOW:";
319 const std::string
AccessibilityTreeFormatter::GetDenyString() {
323 } // namespace content