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 "base/logging.h"
8#include "base/memory/scoped_ptr.h"
9#include "base/strings/string_number_conversions.h"
10#include "base/strings/string_util.h"
11#include "base/strings/stringprintf.h"
12#include "base/strings/utf_string_conversions.h"
13#include "content/browser/accessibility/browser_accessibility_manager.h"
14#include "content/port/browser/render_widget_host_view_port.h"
15#include "content/public/browser/render_view_host.h"
16#include "content/public/browser/web_contents.h"
17
18namespace content {
19namespace {
20const int kIndentSpaces = 4;
21const char* kSkipString = "@NO_DUMP";
22const char* kChildrenDictAttr = "children";
23}
24
25AccessibilityTreeFormatter::AccessibilityTreeFormatter(
26    BrowserAccessibility* root)
27    : root_(root) {
28  Initialize();
29}
30
31// static
32AccessibilityTreeFormatter* AccessibilityTreeFormatter::Create(
33    RenderViewHost* rvh) {
34  RenderWidgetHostViewPort* host_view = static_cast<RenderWidgetHostViewPort*>(
35      WebContents::FromRenderViewHost(rvh)->GetRenderWidgetHostView());
36
37  BrowserAccessibilityManager* manager =
38      host_view->GetBrowserAccessibilityManager();
39  if (!manager)
40    return NULL;
41
42  BrowserAccessibility* root = manager->GetRoot();
43  return new AccessibilityTreeFormatter(root);
44}
45
46
47AccessibilityTreeFormatter::~AccessibilityTreeFormatter() {
48}
49
50scoped_ptr<base::DictionaryValue>
51AccessibilityTreeFormatter::BuildAccessibilityTree() {
52  scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
53  RecursiveBuildAccessibilityTree(*root_, dict.get());
54  return dict.Pass();
55}
56
57void AccessibilityTreeFormatter::FormatAccessibilityTree(
58    base::string16* contents) {
59  scoped_ptr<base::DictionaryValue> dict = BuildAccessibilityTree();
60  RecursiveFormatAccessibilityTree(*(dict.get()), contents);
61}
62
63void AccessibilityTreeFormatter::RecursiveBuildAccessibilityTree(
64    const BrowserAccessibility& node, base::DictionaryValue* dict) {
65  AddProperties(node, dict);
66
67  base::ListValue* children = new base::ListValue;
68  dict->Set(kChildrenDictAttr, children);
69
70  for (size_t i = 0; i < node.PlatformChildCount(); ++i) {
71    BrowserAccessibility* child_node = node.children()[i];
72    base::DictionaryValue* child_dict = new base::DictionaryValue;
73    children->Append(child_dict);
74    RecursiveBuildAccessibilityTree(*child_node, child_dict);
75  }
76}
77
78void AccessibilityTreeFormatter::RecursiveFormatAccessibilityTree(
79    const base::DictionaryValue& dict, base::string16* contents, int depth) {
80  base::string16 line =
81      ToString(dict, base::string16(depth * kIndentSpaces, ' '));
82  if (line.find(ASCIIToUTF16(kSkipString)) != base::string16::npos)
83    return;
84
85  *contents += line;
86  const base::ListValue* children;
87  dict.GetList(kChildrenDictAttr, &children);
88  const base::DictionaryValue* child_dict;
89  for (size_t i = 0; i < children->GetSize(); i++) {
90    children->GetDictionary(i, &child_dict);
91    RecursiveFormatAccessibilityTree(*child_dict, contents, depth + 1);
92  }
93}
94
95#if (!defined(OS_WIN) && !defined(OS_MACOSX) && !defined(OS_ANDROID) && \
96     !defined(TOOLKIT_GTK))
97void AccessibilityTreeFormatter::AddProperties(const BrowserAccessibility& node,
98                                               base::DictionaryValue* dict) {
99  dict->SetInteger("id", node.renderer_id());
100}
101
102base::string16 AccessibilityTreeFormatter::ToString(
103    const base::DictionaryValue& node,
104    const base::string16& indent) {
105  int id_value;
106  node.GetInteger("id", &id_value);
107  return indent + base::IntToString16(id_value) +
108       ASCIIToUTF16("\n");
109}
110
111void AccessibilityTreeFormatter::Initialize() {}
112
113// static
114const base::FilePath::StringType
115AccessibilityTreeFormatter::GetActualFileSuffix() {
116  return base::FilePath::StringType();
117}
118
119// static
120const base::FilePath::StringType
121AccessibilityTreeFormatter::GetExpectedFileSuffix() {
122  return base::FilePath::StringType();
123}
124
125// static
126const std::string AccessibilityTreeFormatter::GetAllowEmptyString() {
127  return std::string();
128}
129
130// static
131const std::string AccessibilityTreeFormatter::GetAllowString() {
132  return std::string();
133}
134
135// static
136const std::string AccessibilityTreeFormatter::GetDenyString() {
137  return std::string();
138}
139#endif
140
141void AccessibilityTreeFormatter::SetFilters(
142    const std::vector<Filter>& filters) {
143  filters_ = filters;
144}
145
146bool AccessibilityTreeFormatter::MatchesFilters(
147    const base::string16& text, bool default_result) const {
148  std::vector<Filter>::const_iterator iter = filters_.begin();
149  bool allow = default_result;
150  for (iter = filters_.begin(); iter != filters_.end(); ++iter) {
151    if (MatchPattern(text, iter->match_str)) {
152      if (iter->type == Filter::ALLOW_EMPTY)
153        allow = true;
154      else if (iter->type == Filter::ALLOW)
155        allow = (!MatchPattern(text, UTF8ToUTF16("*=''")));
156      else
157        allow = false;
158    }
159  }
160  return allow;
161}
162
163base::string16 AccessibilityTreeFormatter::FormatCoordinates(
164    const char* name, const char* x_name, const char* y_name,
165    const base::DictionaryValue& value) {
166  int x, y;
167  value.GetInteger(x_name, &x);
168  value.GetInteger(y_name, &y);
169  std::string xy_str(base::StringPrintf("%s=(%d, %d)", name, x, y));
170
171  return UTF8ToUTF16(xy_str);
172}
173
174void AccessibilityTreeFormatter::WriteAttribute(
175    bool include_by_default, const std::string& attr, base::string16* line) {
176  WriteAttribute(include_by_default, UTF8ToUTF16(attr), line);
177}
178
179void AccessibilityTreeFormatter::WriteAttribute(
180    bool include_by_default, const base::string16& attr, base::string16* line) {
181  if (attr.empty())
182    return;
183  if (!MatchesFilters(attr, include_by_default))
184    return;
185  if (!line->empty())
186    *line += ASCIIToUTF16(" ");
187  *line += attr;
188}
189
190}  // namespace content
191