1 // Copyright 2013 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_base.h"
6 
7 #include <string>
8 
9 #include "base/android/jni_android.h"
10 #include "base/android/jni_string.h"
11 #include "base/files/file_path.h"
12 #include "base/json/json_writer.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "content/browser/accessibility/browser_accessibility_android.h"
19 
20 using base::StringPrintf;
21 
22 namespace content {
23 
24 namespace {
25 // clang-format off
26 const char* const BOOL_ATTRIBUTES[] = {
27     "checkable",
28     "checked",
29     "clickable",
30     "collection",
31     "collection_item",
32     "content_invalid",
33     "disabled",
34     "dismissable",
35     "editable_text",
36     "focusable",
37     "focused",
38     "has_character_locations",
39     "has_image",
40     "has_non_empty_value",
41     "heading",
42     "hierarchical",
43     "invisible",
44     "link",
45     "multiline",
46     "multiselectable",
47     "password",
48     "range",
49     "scrollable",
50     "selected",
51     "interesting"
52 };
53 
54 const char* const STRING_ATTRIBUTES[] = {
55     "name",
56     "hint",
57     "state_description",
58 };
59 
60 const char* const INT_ATTRIBUTES[] = {
61     "item_index",
62     "item_count",
63     "row_count",
64     "column_count",
65     "row_index",
66     "row_span",
67     "column_index",
68     "column_span",
69     "input_type",
70     "live_region_type",
71     "range_min",
72     "range_max",
73     "range_current_value",
74     "text_change_added_count",
75     "text_change_removed_count",
76 };
77 // clang-format on
78 }  // namespace
79 
80 class AccessibilityTreeFormatterAndroid
81     : public AccessibilityTreeFormatterBase {
82  public:
83   AccessibilityTreeFormatterAndroid();
84   ~AccessibilityTreeFormatterAndroid() override;
85 
86   std::unique_ptr<base::DictionaryValue> BuildAccessibilityTree(
87       BrowserAccessibility* root) override;
88 
89   base::Value BuildTreeForWindow(gfx::AcceleratedWidget widget) const override;
90 
91   base::Value BuildTreeForSelector(
92       const AXTreeSelector& selector) const override;
93 
94   void AddDefaultFilters(
95       std::vector<AXPropertyFilter>* property_filters) override;
96 
97  private:
98   void RecursiveBuildAccessibilityTree(const BrowserAccessibility& node,
99                                        base::DictionaryValue* dict) const;
100 
101   void AddProperties(const BrowserAccessibility& node,
102                      base::DictionaryValue* dict) const;
103 
104   std::string ProcessTreeForOutput(
105       const base::DictionaryValue& node,
106       base::DictionaryValue* filtered_dict_result = nullptr) override;
107 };
108 
109 // static
Create()110 std::unique_ptr<ui::AXTreeFormatter> AccessibilityTreeFormatter::Create() {
111   return std::make_unique<AccessibilityTreeFormatterAndroid>();
112 }
113 
114 // static
115 std::vector<AccessibilityTreeFormatter::TestPass>
GetTestPasses()116 AccessibilityTreeFormatter::GetTestPasses() {
117   // Note: Android doesn't do a "blink" pass; the blink tree is different on
118   // Android because we exclude inline text boxes, for performance.
119   return {
120       {"android", &AccessibilityTreeFormatter::Create},
121   };
122 }
123 
AccessibilityTreeFormatterAndroid()124 AccessibilityTreeFormatterAndroid::AccessibilityTreeFormatterAndroid() {}
125 
~AccessibilityTreeFormatterAndroid()126 AccessibilityTreeFormatterAndroid::~AccessibilityTreeFormatterAndroid() {}
127 
128 std::unique_ptr<base::DictionaryValue>
BuildAccessibilityTree(BrowserAccessibility * root)129 AccessibilityTreeFormatterAndroid::BuildAccessibilityTree(
130     BrowserAccessibility* root) {
131   CHECK(root);
132   std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
133 
134   // XXX: Android formatter should walk native Android tree (not internal one).
135   RecursiveBuildAccessibilityTree(*root, dict.get());
136   return dict;
137 }
138 
BuildTreeForWindow(gfx::AcceleratedWidget widget) const139 base::Value AccessibilityTreeFormatterAndroid::BuildTreeForWindow(
140     gfx::AcceleratedWidget widget) const {
141   NOTREACHED();
142   return base::Value(base::Value::Type::DICTIONARY);
143 }
144 
BuildTreeForSelector(const AXTreeSelector & selector) const145 base::Value AccessibilityTreeFormatterAndroid::BuildTreeForSelector(
146     const AXTreeSelector& selector) const {
147   NOTREACHED();
148   return base::Value(base::Value::Type::DICTIONARY);
149 }
150 
AddDefaultFilters(std::vector<AXPropertyFilter> * property_filters)151 void AccessibilityTreeFormatterAndroid::AddDefaultFilters(
152     std::vector<AXPropertyFilter>* property_filters) {
153   AddPropertyFilter(property_filters, "hint=*");
154   AddPropertyFilter(property_filters, "interesting", AXPropertyFilter::DENY);
155   AddPropertyFilter(property_filters, "has_character_locations",
156                     AXPropertyFilter::DENY);
157   AddPropertyFilter(property_filters, "has_image", AXPropertyFilter::DENY);
158 }
159 
RecursiveBuildAccessibilityTree(const BrowserAccessibility & node,base::DictionaryValue * dict) const160 void AccessibilityTreeFormatterAndroid::RecursiveBuildAccessibilityTree(
161     const BrowserAccessibility& node,
162     base::DictionaryValue* dict) const {
163   AddProperties(node, dict);
164 
165   auto children = std::make_unique<base::ListValue>();
166 
167   for (size_t i = 0; i < node.PlatformChildCount(); ++i) {
168     BrowserAccessibility* child_node = node.PlatformGetChild(i);
169     std::unique_ptr<base::DictionaryValue> child_dict(
170         new base::DictionaryValue);
171     RecursiveBuildAccessibilityTree(*child_node, child_dict.get());
172     children->Append(std::move(child_dict));
173   }
174   dict->Set(kChildrenDictAttr, std::move(children));
175 }
176 
AddProperties(const BrowserAccessibility & node,base::DictionaryValue * dict) const177 void AccessibilityTreeFormatterAndroid::AddProperties(
178     const BrowserAccessibility& node,
179     base::DictionaryValue* dict) const {
180   dict->SetInteger("id", node.GetId());
181 
182   const BrowserAccessibilityAndroid* android_node =
183       static_cast<const BrowserAccessibilityAndroid*>(&node);
184 
185   // Class name.
186   dict->SetString("class", android_node->GetClassName());
187 
188   // Bool attributes.
189   dict->SetBoolean("checkable", android_node->IsCheckable());
190   dict->SetBoolean("checked", android_node->IsChecked());
191   dict->SetBoolean("clickable", android_node->IsClickable());
192   dict->SetBoolean("collection", android_node->IsCollection());
193   dict->SetBoolean("collection_item", android_node->IsCollectionItem());
194   dict->SetBoolean("disabled", !android_node->IsEnabled());
195   dict->SetBoolean("dismissable", android_node->IsDismissable());
196   dict->SetBoolean("editable_text", android_node->IsTextField());
197   dict->SetBoolean("focusable", android_node->IsFocusable());
198   dict->SetBoolean("focused", android_node->IsFocused());
199   dict->SetBoolean("has_character_locations",
200                    android_node->HasCharacterLocations());
201   dict->SetBoolean("has_image", android_node->HasImage());
202   dict->SetBoolean("has_non_empty_value", android_node->HasNonEmptyValue());
203   dict->SetBoolean("heading", android_node->IsHeading());
204   dict->SetBoolean("hierarchical", android_node->IsHierarchical());
205   dict->SetBoolean("invisible", !android_node->IsVisibleToUser());
206   dict->SetBoolean("link", android_node->IsLink());
207   dict->SetBoolean("multiline", android_node->IsMultiLine());
208   dict->SetBoolean("multiselectable", android_node->IsMultiselectable());
209   dict->SetBoolean("range", android_node->GetData().IsRangeValueSupported());
210   dict->SetBoolean("password", android_node->IsPasswordField());
211   dict->SetBoolean("scrollable", android_node->IsScrollable());
212   dict->SetBoolean("selected", android_node->IsSelected());
213   dict->SetBoolean("interesting", android_node->IsInterestingOnAndroid());
214 
215   // String attributes.
216   dict->SetString("name", android_node->GetInnerText());
217   dict->SetString("hint", android_node->GetHint());
218   dict->SetString("role_description", android_node->GetRoleDescription());
219   dict->SetString("state_description", android_node->GetStateDescription());
220 
221   // Int attributes.
222   dict->SetInteger("item_index", android_node->GetItemIndex());
223   dict->SetInteger("item_count", android_node->GetItemCount());
224   dict->SetInteger("row_count", android_node->RowCount());
225   dict->SetInteger("column_count", android_node->ColumnCount());
226   dict->SetInteger("row_index", android_node->RowIndex());
227   dict->SetInteger("row_span", android_node->RowSpan());
228   dict->SetInteger("column_index", android_node->ColumnIndex());
229   dict->SetInteger("column_span", android_node->ColumnSpan());
230   dict->SetInteger("input_type", android_node->AndroidInputType());
231   dict->SetInteger("live_region_type", android_node->AndroidLiveRegionType());
232   dict->SetInteger("range_min", static_cast<int>(android_node->RangeMin()));
233   dict->SetInteger("range_max", static_cast<int>(android_node->RangeMax()));
234   dict->SetInteger("range_current_value",
235                    static_cast<int>(android_node->RangeCurrentValue()));
236   dict->SetInteger("text_change_added_count",
237                    android_node->GetTextChangeAddedCount());
238   dict->SetInteger("text_change_removed_count",
239                    android_node->GetTextChangeRemovedCount());
240 
241   // Actions.
242   dict->SetBoolean("action_scroll_forward", android_node->CanScrollForward());
243   dict->SetBoolean("action_scroll_backward", android_node->CanScrollBackward());
244   dict->SetBoolean("action_scroll_up", android_node->CanScrollUp());
245   dict->SetBoolean("action_scroll_down", android_node->CanScrollDown());
246   dict->SetBoolean("action_scroll_left", android_node->CanScrollLeft());
247   dict->SetBoolean("action_scroll_right", android_node->CanScrollRight());
248 }
249 
ProcessTreeForOutput(const base::DictionaryValue & dict,base::DictionaryValue * filtered_dict_result)250 std::string AccessibilityTreeFormatterAndroid::ProcessTreeForOutput(
251     const base::DictionaryValue& dict,
252     base::DictionaryValue* filtered_dict_result) {
253   std::string error_value;
254   if (dict.GetString("error", &error_value))
255     return error_value;
256 
257   std::string line;
258   if (show_ids()) {
259     int id_value;
260     dict.GetInteger("id", &id_value);
261     WriteAttribute(true, base::NumberToString(id_value), &line);
262   }
263 
264   std::string class_value;
265   dict.GetString("class", &class_value);
266   WriteAttribute(true, class_value, &line);
267 
268   std::string role_description;
269   dict.GetString("role_description", &role_description);
270   if (!role_description.empty()) {
271     WriteAttribute(
272         true, StringPrintf("role_description='%s'", role_description.c_str()),
273         &line);
274   }
275 
276   for (unsigned i = 0; i < base::size(BOOL_ATTRIBUTES); i++) {
277     const char* attribute_name = BOOL_ATTRIBUTES[i];
278     bool value;
279     if (dict.GetBoolean(attribute_name, &value) && value)
280       WriteAttribute(true, attribute_name, &line);
281   }
282 
283   for (unsigned i = 0; i < base::size(STRING_ATTRIBUTES); i++) {
284     const char* attribute_name = STRING_ATTRIBUTES[i];
285     std::string value;
286     if (!dict.GetString(attribute_name, &value) || value.empty())
287       continue;
288     WriteAttribute(true, StringPrintf("%s='%s'", attribute_name, value.c_str()),
289                    &line);
290   }
291 
292   for (unsigned i = 0; i < base::size(INT_ATTRIBUTES); i++) {
293     const char* attribute_name = INT_ATTRIBUTES[i];
294     int value;
295     if (!dict.GetInteger(attribute_name, &value) || value == 0)
296       continue;
297     WriteAttribute(true, StringPrintf("%s=%d", attribute_name, value), &line);
298   }
299 
300   return line;
301 }
302 
303 }  // namespace content
304