Chromium Code Reviews| OLD | NEW |
|---|---|
| (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_snapshot_node_android_platform.h" | |
| 6 | |
| 7 #include <memory> | |
|
Luis Héctor Chávez
2017/04/25 18:22:36
nit: you don't need this since you're already incl
Muyuan
2017/04/25 19:38:13
Done.
| |
| 8 #include <string> | |
| 9 | |
| 10 #include "base/logging.h" | |
| 11 #include "base/memory/ptr_util.h" | |
| 12 #include "ui/accessibility/ax_export.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 base::string16 GetText(const AXNode* node) { | |
| 24 if (node->IsTextNode()) { | |
| 25 return node->data().GetString16Attribute(ui::AX_ATTR_NAME); | |
| 26 } | |
| 27 base::string16 text; | |
| 28 for (auto* child : node->children()) { | |
| 29 text += GetText(child); | |
| 30 } | |
| 31 return text; | |
| 32 } | |
| 33 | |
| 34 bool HasFocusableChild(const AXNode* node) { | |
| 35 for (auto* child : node->children()) { | |
| 36 if ((child->data().state & ui::AX_STATE_FOCUSABLE) != 0 || | |
| 37 HasFocusableChild(child)) { | |
| 38 return true; | |
| 39 } | |
| 40 } | |
| 41 return false; | |
| 42 } | |
| 43 | |
| 44 bool IsLeaf(const AXNode* node) { | |
| 45 if (node->child_count() == 0) | |
| 46 return true; | |
| 47 | |
| 48 if (node->IsTextNode()) { | |
| 49 return true; | |
| 50 } | |
| 51 | |
| 52 switch (node->data().role) { | |
| 53 case ui::AX_ROLE_IMAGE: | |
| 54 case ui::AX_ROLE_METER: | |
| 55 case ui::AX_ROLE_SCROLL_BAR: | |
| 56 case ui::AX_ROLE_SLIDER: | |
| 57 case ui::AX_ROLE_SPLITTER: | |
| 58 case ui::AX_ROLE_PROGRESS_INDICATOR: | |
| 59 case ui::AX_ROLE_DATE: | |
| 60 case ui::AX_ROLE_DATE_TIME: | |
| 61 case ui::AX_ROLE_INPUT_TIME: | |
| 62 return true; | |
| 63 default: | |
| 64 return false; | |
| 65 } | |
| 66 } | |
| 67 | |
| 68 gfx::Rect RelativeToAbsoluteBounds(const AXNode* node, | |
| 69 gfx::RectF bounds, | |
| 70 const AXTree* tree) { | |
| 71 const AXNode* current = node; | |
| 72 while (current != nullptr) { | |
| 73 if (current->data().transform) | |
| 74 current->data().transform->TransformRect(&bounds); | |
| 75 auto* container = tree->GetFromId(current->data().offset_container_id); | |
| 76 if (!container) { | |
| 77 if (current == tree->root()) | |
| 78 container = current->parent(); | |
| 79 else | |
| 80 container = tree->root(); | |
| 81 } | |
| 82 if (!container || container == current) | |
| 83 break; | |
| 84 | |
| 85 gfx::RectF container_bounds = container->data().location; | |
| 86 bounds.Offset(container_bounds.x(), container_bounds.y()); | |
| 87 current = container; | |
| 88 } | |
| 89 return gfx::ToEnclosingRect(bounds); | |
| 90 } | |
| 91 | |
| 92 void FixEmptyBounds(const AXNode* node, gfx::RectF* bounds, const AXTree* tree); | |
| 93 | |
| 94 gfx::Rect GetPageBoundsRect(const AXNode* node, const AXTree* tree) { | |
| 95 gfx::RectF bounds = node->data().location; | |
| 96 FixEmptyBounds(node, &bounds, tree); | |
| 97 return RelativeToAbsoluteBounds(node, bounds, tree); | |
| 98 } | |
| 99 | |
| 100 void FixEmptyBounds(const AXNode* node, | |
| 101 gfx::RectF* bounds, | |
| 102 const AXTree* tree) { | |
| 103 if (bounds->width() > 0 && bounds->height() > 0) | |
| 104 return; | |
| 105 for (auto* child : node->children()) { | |
| 106 gfx::Rect child_bounds = GetPageBoundsRect(child, tree); | |
| 107 if (child_bounds.width() == 0 || child_bounds.height() == 0) | |
| 108 continue; | |
| 109 if (bounds->width() == 0 || bounds->height() == 0) { | |
| 110 *bounds = gfx::RectF(child_bounds); | |
| 111 continue; | |
| 112 } | |
| 113 bounds->Union(gfx::RectF(child_bounds)); | |
| 114 } | |
| 115 } | |
| 116 | |
| 117 } // namespace | |
| 118 | |
| 119 AXSnapshotNodeAndroid::AXSnapshotNodeAndroid() = default; | |
| 120 AX_EXPORT AXSnapshotNodeAndroid::~AXSnapshotNodeAndroid() = default; | |
| 121 | |
| 122 // static | |
| 123 AX_EXPORT std::unique_ptr<AXSnapshotNodeAndroid> AXSnapshotNodeAndroid::Create( | |
| 124 const AXTreeUpdate& update) { | |
| 125 auto tree = base::MakeUnique<ui::AXSerializableTree>(); | |
| 126 if (!tree->Unserialize(update)) { | |
| 127 LOG(FATAL) << tree->error(); | |
| 128 } | |
| 129 return WalkAXTreeDepthFirst(tree->root(), gfx::Rect(), update, tree.get()); | |
| 130 } | |
| 131 | |
| 132 // static | |
| 133 AX_EXPORT const char* AXSnapshotNodeAndroid::AXRoleToAndroidClassName( | |
| 134 AXRole role, | |
| 135 bool has_parent) { | |
| 136 switch (role) { | |
| 137 case ui::AX_ROLE_SEARCH_BOX: | |
| 138 case ui::AX_ROLE_SPIN_BUTTON: | |
| 139 case ui::AX_ROLE_TEXT_FIELD: | |
| 140 return ui::kAXEditTextClassname; | |
| 141 case ui::AX_ROLE_SLIDER: | |
| 142 return ui::kAXSeekBarClassname; | |
| 143 case ui::AX_ROLE_COLOR_WELL: | |
| 144 case ui::AX_ROLE_COMBO_BOX: | |
| 145 case ui::AX_ROLE_DATE: | |
| 146 case ui::AX_ROLE_POP_UP_BUTTON: | |
| 147 case ui::AX_ROLE_INPUT_TIME: | |
| 148 return ui::kAXSpinnerClassname; | |
| 149 case ui::AX_ROLE_BUTTON: | |
| 150 case ui::AX_ROLE_MENU_BUTTON: | |
| 151 return ui::kAXButtonClassname; | |
| 152 case ui::AX_ROLE_CHECK_BOX: | |
| 153 case ui::AX_ROLE_SWITCH: | |
| 154 return ui::kAXCheckBoxClassname; | |
| 155 case ui::AX_ROLE_RADIO_BUTTON: | |
| 156 return ui::kAXRadioButtonClassname; | |
| 157 case ui::AX_ROLE_TOGGLE_BUTTON: | |
| 158 return ui::kAXToggleButtonClassname; | |
| 159 case ui::AX_ROLE_CANVAS: | |
| 160 case ui::AX_ROLE_IMAGE: | |
| 161 case ui::AX_ROLE_SVG_ROOT: | |
| 162 return ui::kAXImageClassname; | |
| 163 case ui::AX_ROLE_METER: | |
| 164 case ui::AX_ROLE_PROGRESS_INDICATOR: | |
| 165 return ui::kAXProgressBarClassname; | |
| 166 case ui::AX_ROLE_TAB_LIST: | |
| 167 return ui::kAXTabWidgetClassname; | |
| 168 case ui::AX_ROLE_GRID: | |
| 169 case ui::AX_ROLE_TREE_GRID: | |
| 170 case ui::AX_ROLE_TABLE: | |
| 171 return ui::kAXGridViewClassname; | |
| 172 case ui::AX_ROLE_LIST: | |
| 173 case ui::AX_ROLE_LIST_BOX: | |
| 174 case ui::AX_ROLE_DESCRIPTION_LIST: | |
| 175 return ui::kAXListViewClassname; | |
| 176 case ui::AX_ROLE_DIALOG: | |
| 177 return ui::kAXDialogClassname; | |
| 178 case ui::AX_ROLE_ROOT_WEB_AREA: | |
| 179 return has_parent ? ui::kAXWebViewClassname : ui::kAXViewClassname; | |
| 180 case ui::AX_ROLE_MENU_ITEM: | |
| 181 case ui::AX_ROLE_MENU_ITEM_CHECK_BOX: | |
| 182 case ui::AX_ROLE_MENU_ITEM_RADIO: | |
| 183 return ui::kAXMenuItemClassname; | |
| 184 default: | |
| 185 return ui::kAXViewClassname; | |
| 186 } | |
| 187 } | |
| 188 | |
| 189 // static | |
| 190 std::unique_ptr<AXSnapshotNodeAndroid> | |
| 191 AXSnapshotNodeAndroid::WalkAXTreeDepthFirst(const AXNode* node, | |
| 192 gfx::Rect rect, | |
| 193 const ui::AXTreeUpdate& update, | |
| 194 const AXTree* tree) { | |
| 195 auto result = | |
| 196 std::unique_ptr<AXSnapshotNodeAndroid>(new AXSnapshotNodeAndroid); | |
|
Luis Héctor Chávez
2017/04/25 18:22:36
nit: prefer base::make_unique<AXSnapshotNodeAndroi
Muyuan
2017/04/25 19:38:13
OK.. actually it was there to address the comment:
Luis Héctor Chávez
2017/04/25 20:05:32
As discussed offline, either parens or a comment a
Muyuan
2017/04/25 20:49:42
Done.
| |
| 197 result->text = GetText(node); | |
| 198 result->class_name = AXSnapshotNodeAndroid::AXRoleToAndroidClassName( | |
| 199 node->data().role, node->parent() != nullptr); | |
| 200 | |
| 201 result->text_size = -1.0; | |
| 202 result->bgcolor = 0; | |
| 203 result->color = 0; | |
| 204 result->bold = 0; | |
| 205 result->italic = 0; | |
| 206 result->line_through = 0; | |
| 207 result->underline = 0; | |
| 208 | |
| 209 if (node->data().HasFloatAttribute(ui::AX_ATTR_FONT_SIZE)) { | |
| 210 gfx::RectF text_size_rect( | |
| 211 0, 0, 1, node->data().GetFloatAttribute(ui::AX_ATTR_FONT_SIZE)); | |
| 212 gfx::Rect scaled_text_size_rect = | |
| 213 RelativeToAbsoluteBounds(node, text_size_rect, tree); | |
| 214 result->text_size = scaled_text_size_rect.height(); | |
| 215 | |
| 216 const int text_style = node->data().GetIntAttribute(ui::AX_ATTR_TEXT_STYLE); | |
| 217 result->color = node->data().GetIntAttribute(ui::AX_ATTR_COLOR); | |
| 218 result->bgcolor = | |
| 219 node->data().GetIntAttribute(ui::AX_ATTR_BACKGROUND_COLOR); | |
| 220 result->bold = (text_style & ui::AX_TEXT_STYLE_BOLD) != 0; | |
| 221 result->italic = (text_style & ui::AX_TEXT_STYLE_ITALIC) != 0; | |
| 222 result->line_through = (text_style & ui::AX_TEXT_STYLE_LINE_THROUGH) != 0; | |
| 223 result->underline = (text_style & ui::AX_TEXT_STYLE_ITALIC) != 0; | |
| 224 } | |
| 225 | |
| 226 const gfx::Rect& absolute_rect = GetPageBoundsRect(node, tree); | |
| 227 gfx::Rect parent_relative_rect = absolute_rect; | |
| 228 bool is_root = node->parent() == nullptr; | |
| 229 if (!is_root) { | |
| 230 parent_relative_rect.Offset(-rect.OffsetFromOrigin()); | |
| 231 } | |
| 232 result->rect = gfx::Rect(parent_relative_rect.x(), parent_relative_rect.y(), | |
| 233 absolute_rect.width(), absolute_rect.height()); | |
| 234 result->has_selection = false; | |
| 235 | |
| 236 if (IsLeaf(node)) { | |
| 237 int start_selection = 0; | |
| 238 int end_selection = 0; | |
| 239 if (update.tree_data.sel_anchor_object_id == node->data().id) { | |
| 240 start_selection = update.tree_data.sel_anchor_offset; | |
| 241 end_selection = GetText(node).length(); | |
| 242 } | |
| 243 if (update.tree_data.sel_focus_object_id == node->data().id) { | |
| 244 end_selection = update.tree_data.sel_focus_offset; | |
| 245 } | |
| 246 if (end_selection > 0) { | |
| 247 result->has_selection = true; | |
| 248 result->start_selection = start_selection; | |
| 249 result->end_selection = end_selection; | |
| 250 } | |
| 251 } | |
| 252 | |
| 253 for (auto* child : node->children()) { | |
| 254 result->children.push_back( | |
| 255 WalkAXTreeDepthFirst(child, absolute_rect, update, tree)); | |
| 256 } | |
| 257 | |
| 258 return result; | |
| 259 } | |
| 260 | |
| 261 } // namespace ui | |
| OLD | NEW |