Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(63)

Side by Side Diff: ui/accessibility/platform/ax_android_snapshot.cc

Issue 2808383004: Refactor and send voice interaction structure (Closed)
Patch Set: fix WmShell position Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 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 "ui/accessibility/platform/ax_android_snapshot.h"
6
7 #include <memory>
8 #include <string>
9
10 #include "base/logging.h"
11 #include "base/memory/ptr_util.h"
12 #include "ui/accessibility/ax_enums.h"
13 #include "ui/accessibility/ax_node.h"
14 #include "ui/accessibility/ax_serializable_tree.h"
15 #include "ui/accessibility/platform/ax_android_constants.h"
16 #include "ui/gfx/geometry/rect_conversions.h"
17 #include "ui/gfx/transform.h"
18
19 namespace ui {
20
21 namespace {
22
23 bool HasFocusableChild(const AXNode* node) {
24 for (auto* child : node->children()) {
25 if ((child->data().state & ui::AX_STATE_FOCUSABLE) != 0 ||
26 HasFocusableChild(child)) {
27 return true;
28 }
29 }
30 return false;
31 }
32
33 bool IsLeaf(const AXNode* node) {
34 if (node->child_count() == 0)
35 return true;
36
37 if (node->IsTextNode()) {
38 return true;
39 }
40
41 switch (node->data().role) {
42 case ui::AX_ROLE_IMAGE:
43 case ui::AX_ROLE_METER:
44 case ui::AX_ROLE_SCROLL_BAR:
45 case ui::AX_ROLE_SLIDER:
46 case ui::AX_ROLE_SPLITTER:
47 case ui::AX_ROLE_PROGRESS_INDICATOR:
48 case ui::AX_ROLE_DATE:
49 case ui::AX_ROLE_DATE_TIME:
50 case ui::AX_ROLE_INPUT_TIME:
51 return true;
52 default:
53 return false;
54 }
55 }
56
57 std::unique_ptr<AXSnapshotNodeAndroid> WalkAXTreeDepthFirst(
58 const AXNode* node,
59 gfx::Rect rect,
60 const ui::AXTreeUpdate& update,
61 const AXTree* tree) {
62 auto result = base::MakeUnique<AXSnapshotNodeAndroid>();
63 if (node->IsTextNode())
64 result->text = GetText(node);
65 result->class_name = GetClassName(node);
66
67 result->text_size = -1.0;
68 result->bgcolor = 0;
69 result->color = 0;
70 result->bold = 0;
71 result->italic = 0;
72 result->line_through = 0;
73 result->underline = 0;
74
75 if (node->data().HasFloatAttribute(ui::AX_ATTR_FONT_SIZE)) {
76 gfx::RectF text_size_rect(
77 0, 0, 1, node->data().GetFloatAttribute(ui::AX_ATTR_FONT_SIZE));
78 gfx::Rect scaled_text_size_rect =
79 RelativeToAbsoluteBounds(node, text_size_rect, tree);
80 result->text_size = scaled_text_size_rect.height();
81
82 const int text_style = node->data().GetIntAttribute(ui::AX_ATTR_TEXT_STYLE);
83 result->color = node->data().GetIntAttribute(ui::AX_ATTR_COLOR);
84 result->bgcolor =
85 node->data().GetIntAttribute(ui::AX_ATTR_BACKGROUND_COLOR);
86 result->bold = (text_style & ui::AX_TEXT_STYLE_BOLD) != 0;
87 result->italic = (text_style & ui::AX_TEXT_STYLE_ITALIC) != 0;
88 result->line_through = (text_style & ui::AX_TEXT_STYLE_LINE_THROUGH) != 0;
89 result->underline = (text_style & ui::AX_TEXT_STYLE_ITALIC) != 0;
90 }
91
92 const gfx::Rect& absolute_rect = GetPageBoundsRect(node, tree);
93 gfx::Rect parent_relative_rect = absolute_rect;
94 bool is_root = node->parent() == nullptr;
95 if (!is_root) {
96 parent_relative_rect.Offset(-rect.OffsetFromOrigin());
97 }
98 result->rect = gfx::Rect(parent_relative_rect.x(), parent_relative_rect.y(),
99 absolute_rect.width(), absolute_rect.height());
100 result->has_selection = false;
101
102 if (IsLeaf(node)) {
103 int start_selection = 0;
104 int end_selection = 0;
105 if (update.tree_data.sel_anchor_object_id == node->data().id) {
106 start_selection = update.tree_data.sel_anchor_offset;
107 end_selection = GetText(node).length();
108 }
109 if (update.tree_data.sel_focus_object_id == node->data().id) {
110 end_selection = update.tree_data.sel_focus_offset;
111 }
112 if (end_selection > 0) {
113 result->has_selection = true;
114 result->start_selection = start_selection;
115 result->end_selection = end_selection;
116 }
117 }
118
119 for (auto* child : node->children()) {
120 result->children.push_back(
121 WalkAXTreeDepthFirst(child, absolute_rect, update, tree));
122 }
123
124 return result;
125 }
126 }
127
128 AX_EXPORT AXSnapshotNodeAndroid::AXSnapshotNodeAndroid() = default;
129 AX_EXPORT AXSnapshotNodeAndroid::~AXSnapshotNodeAndroid() = default;
130
131 AX_EXPORT std::unique_ptr<AXSnapshotNodeAndroid> AXSnapshotNodeAndroid::Create(
132 const AXTreeUpdate& update) {
133 auto tree = base::MakeUnique<ui::AXSerializableTree>();
134 if (!tree->Unserialize(update)) {
135 LOG(FATAL) << tree->error();
136 }
137 return WalkAXTreeDepthFirst(tree->root(), gfx::Rect(), update, tree.get());
138 }
139
140 std::string GetClassName(const AXNode* node) {
141 switch (node->data().role) {
142 case ui::AX_ROLE_SEARCH_BOX:
143 case ui::AX_ROLE_SPIN_BUTTON:
144 case ui::AX_ROLE_TEXT_FIELD:
145 return ui::kAXEditTextClassname;
146 case ui::AX_ROLE_SLIDER:
147 return ui::kAXSeekBarClassname;
148 case ui::AX_ROLE_COLOR_WELL:
149 case ui::AX_ROLE_COMBO_BOX:
150 case ui::AX_ROLE_DATE:
151 case ui::AX_ROLE_POP_UP_BUTTON:
152 case ui::AX_ROLE_INPUT_TIME:
153 return ui::kAXSpinnerClassname;
154 case ui::AX_ROLE_BUTTON:
155 case ui::AX_ROLE_MENU_BUTTON:
156 return ui::kAXButtonClassname;
157 case ui::AX_ROLE_CHECK_BOX:
158 case ui::AX_ROLE_SWITCH:
159 return ui::kAXCheckBoxClassname;
160 case ui::AX_ROLE_RADIO_BUTTON:
161 return ui::kAXRadioButtonClassname;
162 case ui::AX_ROLE_TOGGLE_BUTTON:
163 return ui::kAXToggleButtonClassname;
164 case ui::AX_ROLE_CANVAS:
165 case ui::AX_ROLE_IMAGE:
166 case ui::AX_ROLE_SVG_ROOT:
167 return ui::kAXImageClassname;
168 case ui::AX_ROLE_METER:
169 case ui::AX_ROLE_PROGRESS_INDICATOR:
170 return ui::kAXProgressBarClassname;
171 case ui::AX_ROLE_TAB_LIST:
172 return ui::kAXTabWidgetClassname;
173 case ui::AX_ROLE_GRID:
174 case ui::AX_ROLE_TABLE:
175 return ui::kAXGridViewClassname;
176 case ui::AX_ROLE_LIST:
177 case ui::AX_ROLE_LIST_BOX:
178 case ui::AX_ROLE_DESCRIPTION_LIST:
179 return ui::kAXListViewClassname;
180 case ui::AX_ROLE_DIALOG:
181 return ui::kAXDialogClassname;
182 case ui::AX_ROLE_ROOT_WEB_AREA:
183 return node->parent() == nullptr ? ui::kAXWebViewClassname
184 : ui::kAXViewClassname;
185 case ui::AX_ROLE_MENU_ITEM:
186 case ui::AX_ROLE_MENU_ITEM_CHECK_BOX:
187 case ui::AX_ROLE_MENU_ITEM_RADIO:
188 return ui::kAXMenuItemClassname;
189 default:
190 return ui::kAXViewClassname;
191 }
192 }
193
194 base::string16 GetText(const AXNode* node) {
195 if (node->IsTextNode()) {
196 return node->data().GetString16Attribute(ui::AX_ATTR_NAME);
197 }
198 base::string16 text;
199 for (auto* child : node->children()) {
200 text += GetText(child);
201 }
202 return text;
203 }
204
205 gfx::Rect GetPageBoundsRect(const AXNode* node, const AXTree* tree) {
206 gfx::RectF bounds = node->data().location;
207 FixEmptyBounds(node, &bounds, tree);
208 return RelativeToAbsoluteBounds(node, bounds, tree);
209 }
210
211 void FixEmptyBounds(const AXNode* node,
212 gfx::RectF* bounds,
213 const AXTree* tree) {
214 if (bounds->width() > 0 && bounds->height() > 0)
215 return;
216 for (auto* child : node->children()) {
217 gfx::Rect child_bounds = GetPageBoundsRect(child, tree);
218 if (child_bounds.width() == 0 || child_bounds.height() == 0)
219 continue;
220 if (bounds->width() == 0 || bounds->height() == 0) {
221 *bounds = gfx::RectF(child_bounds);
222 continue;
223 }
224 bounds->Union(gfx::RectF(child_bounds));
225 }
226 }
227
228 gfx::Rect RelativeToAbsoluteBounds(const AXNode* node,
229 gfx::RectF bounds,
230 const AXTree* tree) {
231 const AXNode* current = node;
232 while (current != nullptr) {
233 if (current->data().transform)
234 current->data().transform->TransformRect(&bounds);
235 auto* container = tree->GetFromId(current->data().offset_container_id);
236 if (!container) {
237 if (current == tree->root())
238 container = current->parent();
239 else
240 container = tree->root();
241 }
242 if (!container || container == current)
243 break;
244
245 gfx::RectF container_bounds = container->data().location;
246 bounds.Offset(container_bounds.x(), container_bounds.y());
247 current = container;
248 }
249 return gfx::ToEnclosingRect(bounds);
250 }
251
252 } // namespace ui
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698