17d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
27d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
37d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)// found in the LICENSE file.
47d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
57d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "content/browser/accessibility/accessibility_tree_formatter.h"
67d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
77d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include <string>
87d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
97d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "base/android/jni_android.h"
107d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "base/android/jni_string.h"
117d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "base/files/file_path.h"
127d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "base/json/json_writer.h"
13eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/strings/string_util.h"
14eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/strings/stringprintf.h"
15eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/strings/utf_string_conversions.h"
167d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "content/browser/accessibility/browser_accessibility_android.h"
177d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
187d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)using base::StringPrintf;
197d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
207d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)namespace content {
217d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
227d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)namespace {
237d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)const char* BOOL_ATTRIBUTES[] = {
247d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  "checkable",
257d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  "checked",
267d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  "clickable",
27a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  "collection",
28a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  "collection_item",
29a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  "content_invalid",
307d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  "disabled",
31a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  "dismissable",
327d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  "editable_text",
337d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  "focusable",
347d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  "focused",
35a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  "heading",
36a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  "hierarchical",
377d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  "invisible",
38a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  "multiline",
397d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  "password",
40a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  "range",
417d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  "scrollable",
427d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  "selected"
437d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)};
447d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
457d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)const char* STRING_ATTRIBUTES[] = {
467d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  "name"
477d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)};
487d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
497d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)const char* INT_ATTRIBUTES[] = {
507d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  "item_index",
51a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  "item_count",
52a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  "row_count",
53a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  "column_count",
54a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  "row_index",
55a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  "row_span",
56a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  "column_index",
57a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  "column_span",
58a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  "input_type",
59a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  "live_region_type",
60a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  "range_min",
61a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  "range_max",
62a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  "range_current_value",
637d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)};
647d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)}
657d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
667d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)void AccessibilityTreeFormatter::Initialize() {
677d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)}
687d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
697d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)void AccessibilityTreeFormatter::AddProperties(
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const BrowserAccessibility& node, base::DictionaryValue* dict) {
717d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  const BrowserAccessibilityAndroid* android_node =
727d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      static_cast<const BrowserAccessibilityAndroid*>(&node);
737d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
747d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  // Class name.
75eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  dict->SetString("class", android_node->GetClassName());
767d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
777d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  // Bool attributes.
78eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  dict->SetBoolean("checkable", android_node->IsCheckable());
79eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  dict->SetBoolean("checked", android_node->IsChecked());
80a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  dict->SetBoolean("clickable", android_node->IsClickable());
81a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  dict->SetBoolean("collection", android_node->IsCollection());
82a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  dict->SetBoolean("collection_item", android_node->IsCollectionItem());
83eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  dict->SetBoolean("disabled", !android_node->IsEnabled());
84a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  dict->SetBoolean("dismissable", android_node->IsDismissable());
85a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  dict->SetBoolean("editable_text", android_node->IsEditableText());
86a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  dict->SetBoolean("focusable", android_node->IsFocusable());
87a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  dict->SetBoolean("focused", android_node->IsFocused());
88a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  dict->SetBoolean("heading", android_node->IsHeading());
89a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  dict->SetBoolean("hierarchical", android_node->IsHierarchical());
90a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  dict->SetBoolean("invisible", !android_node->IsVisibleToUser());
91a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  dict->SetBoolean("multiline", android_node->IsMultiLine());
92a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  dict->SetBoolean("range", android_node->IsRangeType());
93eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  dict->SetBoolean("password", android_node->IsPassword());
94a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  dict->SetBoolean("scrollable", android_node->IsScrollable());
95eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  dict->SetBoolean("selected", android_node->IsSelected());
967d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
977d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  // String attributes.
98eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  dict->SetString("name", android_node->GetText());
997d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1007d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  // Int attributes.
101eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  dict->SetInteger("item_index", android_node->GetItemIndex());
102eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  dict->SetInteger("item_count", android_node->GetItemCount());
103a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  dict->SetInteger("row_count", android_node->RowCount());
104a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  dict->SetInteger("column_count", android_node->ColumnCount());
105a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  dict->SetInteger("row_index", android_node->RowIndex());
106a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  dict->SetInteger("row_span", android_node->RowSpan());
107a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  dict->SetInteger("column_index", android_node->ColumnIndex());
108a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  dict->SetInteger("column_span", android_node->ColumnSpan());
109a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  dict->SetInteger("input_type", android_node->AndroidInputType());
110a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  dict->SetInteger("live_region_type", android_node->AndroidLiveRegionType());
111a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  dict->SetInteger("range_min", static_cast<int>(android_node->RangeMin()));
112a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  dict->SetInteger("range_max", static_cast<int>(android_node->RangeMax()));
113a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  dict->SetInteger("range_current_value",
114a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                   static_cast<int>(android_node->RangeCurrentValue()));
1157d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)}
1167d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
117a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)base::string16 AccessibilityTreeFormatter::ToString(
1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const base::DictionaryValue& dict,
119a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const base::string16& indent) {
120a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 line;
1217d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
122a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 class_value;
1237d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  dict.GetString("class", &class_value);
1245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  WriteAttribute(true, base::UTF16ToUTF8(class_value), &line);
1257d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1267d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  for (unsigned i = 0; i < arraysize(BOOL_ATTRIBUTES); i++) {
1277d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    const char* attribute_name = BOOL_ATTRIBUTES[i];
1287d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    bool value;
1297d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    if (dict.GetBoolean(attribute_name, &value) && value)
1307d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      WriteAttribute(true, attribute_name, &line);
1317d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  }
1327d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1337d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  for (unsigned i = 0; i < arraysize(STRING_ATTRIBUTES); i++) {
1347d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    const char* attribute_name = STRING_ATTRIBUTES[i];
1357d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    std::string value;
1367d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    if (!dict.GetString(attribute_name, &value) || value.empty())
1377d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      continue;
1387d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    WriteAttribute(true,
1397d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)                   StringPrintf("%s='%s'", attribute_name, value.c_str()),
1407d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)                   &line);
1417d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  }
1427d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1437d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  for (unsigned i = 0; i < arraysize(INT_ATTRIBUTES); i++) {
1447d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    const char* attribute_name = INT_ATTRIBUTES[i];
1457d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    int value;
1467d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    if (!dict.GetInteger(attribute_name, &value) || value == 0)
1477d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)      continue;
1487d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    WriteAttribute(true,
1497d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)                   StringPrintf("%s=%d", attribute_name, value),
1507d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)                   &line);
1517d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  }
1527d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return indent + line + base::ASCIIToUTF16("\n");
1547d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)}
1557d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1567d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)// static
1577d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)const base::FilePath::StringType
1587d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)AccessibilityTreeFormatter::GetActualFileSuffix() {
1597d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  return FILE_PATH_LITERAL("-actual-android.txt");
1607d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)}
1617d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1627d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)// static
1637d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)const base::FilePath::StringType
1647d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)AccessibilityTreeFormatter::GetExpectedFileSuffix() {
1657d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  return FILE_PATH_LITERAL("-expected-android.txt");
1667d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)}
1677d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1687d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)// static
1697d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)const std::string AccessibilityTreeFormatter::GetAllowEmptyString() {
1707d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  return "@ANDROID-ALLOW-EMPTY:";
1717d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)}
1727d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1737d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)// static
1747d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)const std::string AccessibilityTreeFormatter::GetAllowString() {
1757d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  return "@ANDROID-ALLOW:";
1767d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)}
1777d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1787d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)// static
1797d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)const std::string AccessibilityTreeFormatter::GetDenyString() {
1807d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  return "@ANDROID-DENY:";
1817d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)}
1827d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)
1837d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)}  // namespace content
184