Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.h" | 5 #include "chrome/browser/chromeos/arc/accessibility/ax_tree_source_arc.h" |
| 6 | 6 |
| 7 #include <string> | 7 #include <string> |
| 8 | 8 |
| 9 #include "chrome/browser/extensions/api/automation_internal/automation_event_rou ter.h" | 9 #include "chrome/browser/extensions/api/automation_internal/automation_event_rou ter.h" |
| 10 #include "chrome/browser/ui/aura/accessibility/automation_manager_aura.h" | 10 #include "chrome/browser/ui/aura/accessibility/automation_manager_aura.h" |
| 11 #include "chrome/common/extensions/chrome_extension_messages.h" | 11 #include "chrome/common/extensions/chrome_extension_messages.h" |
| 12 #include "components/exo/wm_helper.h" | 12 #include "components/exo/wm_helper.h" |
| 13 #include "ui/accessibility/platform/ax_android_constants.h" | |
| 13 #include "ui/aura/window.h" | 14 #include "ui/aura/window.h" |
| 14 | 15 |
| 15 namespace { | 16 namespace { |
| 16 | 17 |
| 17 ui::AXEvent ToAXEvent(arc::mojom::AccessibilityEventType arc_event_type) { | 18 ui::AXEvent ToAXEvent(arc::mojom::AccessibilityEventType arc_event_type) { |
| 18 switch (arc_event_type) { | 19 switch (arc_event_type) { |
| 19 case arc::mojom::AccessibilityEventType::VIEW_FOCUSED: | 20 case arc::mojom::AccessibilityEventType::VIEW_FOCUSED: |
| 20 case arc::mojom::AccessibilityEventType::VIEW_ACCESSIBILITY_FOCUSED: | 21 case arc::mojom::AccessibilityEventType::VIEW_ACCESSIBILITY_FOCUSED: |
| 21 return ui::AX_EVENT_FOCUS; | 22 return ui::AX_EVENT_FOCUS; |
| 22 case arc::mojom::AccessibilityEventType::VIEW_ACCESSIBILITY_FOCUS_CLEARED: | 23 case arc::mojom::AccessibilityEventType::VIEW_ACCESSIBILITY_FOCUS_CLEARED: |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 63 gfx::Rect bounds_in_screen = node->boundsInScreen; | 64 gfx::Rect bounds_in_screen = node->boundsInScreen; |
| 64 if (focused_window) { | 65 if (focused_window) { |
| 65 aura::Window* toplevel_window = focused_window->GetToplevelWindow(); | 66 aura::Window* toplevel_window = focused_window->GetToplevelWindow(); |
| 66 return gfx::ScaleToEnclosingRect( | 67 return gfx::ScaleToEnclosingRect( |
| 67 bounds_in_screen, | 68 bounds_in_screen, |
| 68 1.0f / toplevel_window->layer()->device_scale_factor()); | 69 1.0f / toplevel_window->layer()->device_scale_factor()); |
| 69 } | 70 } |
| 70 return bounds_in_screen; | 71 return bounds_in_screen; |
| 71 } | 72 } |
| 72 | 73 |
| 74 bool GetBooleanProperty(arc::mojom::AccessibilityNodeInfoData* node, | |
| 75 arc::mojom::AccessibilityBooleanProperty prop) { | |
| 76 if (!node->booleanProperties) | |
| 77 return false; | |
| 78 | |
| 79 auto it = node->booleanProperties->find(prop); | |
| 80 if (it == node->booleanProperties->end()) | |
| 81 return false; | |
| 82 | |
| 83 return it->second; | |
| 84 } | |
| 85 | |
| 73 bool GetStringProperty(arc::mojom::AccessibilityNodeInfoData* node, | 86 bool GetStringProperty(arc::mojom::AccessibilityNodeInfoData* node, |
| 74 arc::mojom::AccessibilityStringProperty prop, | 87 arc::mojom::AccessibilityStringProperty prop, |
| 75 std::string* out_value) { | 88 std::string* out_value) { |
| 76 if (!node->stringProperties) | 89 if (!node->stringProperties) |
| 77 return false; | 90 return false; |
| 78 | 91 |
| 79 auto it = node->stringProperties->find(prop); | 92 auto it = node->stringProperties->find(prop); |
| 80 if (it == node->stringProperties->end()) | 93 if (it == node->stringProperties->end()) |
| 81 return false; | 94 return false; |
| 82 | 95 |
| 83 *out_value = it->second; | 96 *out_value = it->second; |
| 84 return true; | 97 return true; |
| 85 } | 98 } |
| 86 | 99 |
| 100 ui::AXRole GetAXRole(arc::mojom::AccessibilityNodeInfoData* node) { | |
| 101 std::string class_name, text; | |
| 102 GetStringProperty(node, arc::mojom::AccessibilityStringProperty::CLASS_NAME, | |
| 103 &class_name); | |
| 104 GetStringProperty(node, arc::mojom::AccessibilityStringProperty::TEXT, &text); | |
|
yawano
2017/02/28 05:36:07
nit: We can get this later. We are not using the t
| |
| 105 | |
| 106 #define MAP_ROLE(android_class_name, chrome_role) \ | |
|
dmazzoni
2017/02/28 04:31:25
Perhaps these should be a hash_map from string to
David Tseng
2017/02/28 18:52:30
It's not one to one and will probably be less so a
| |
| 107 if (class_name == android_class_name) \ | |
| 108 return chrome_role; | |
| 109 | |
| 110 MAP_ROLE(ui::kAXAbsListViewClassname, ui::AX_ROLE_LIST); | |
| 111 MAP_ROLE(ui::kAXButtonClassname, ui::AX_ROLE_BUTTON); | |
| 112 MAP_ROLE(ui::kAXCheckBoxClassname, ui::AX_ROLE_CHECK_BOX); | |
| 113 MAP_ROLE(ui::kAXCompoundButtonClassname, ui::AX_ROLE_CHECK_BOX); | |
| 114 MAP_ROLE(ui::kAXCheckedTextViewClassname, ui::AX_ROLE_STATIC_TEXT); | |
| 115 MAP_ROLE(ui::kAXDialogClassname, ui::AX_ROLE_DIALOG); | |
| 116 MAP_ROLE(ui::kAXEditTextClassname, ui::AX_ROLE_TEXT_FIELD); | |
| 117 MAP_ROLE(ui::kAXGridViewClassname, ui::AX_ROLE_TABLE); | |
| 118 MAP_ROLE(ui::kAXImageClassname, ui::AX_ROLE_IMAGE); | |
| 119 if (GetBooleanProperty(node, | |
| 120 arc::mojom::AccessibilityBooleanProperty::CLICKABLE)) { | |
| 121 MAP_ROLE(ui::kAXImageViewClassname, ui::AX_ROLE_BUTTON); | |
| 122 } else { | |
| 123 MAP_ROLE(ui::kAXImageViewClassname, ui::AX_ROLE_IMAGE); | |
| 124 } | |
| 125 MAP_ROLE(ui::kAXListViewClassname, ui::AX_ROLE_LIST); | |
| 126 MAP_ROLE(ui::kAXMenuItemClassname, ui::AX_ROLE_MENU_ITEM); | |
| 127 MAP_ROLE(ui::kAXPagerClassname, ui::AX_ROLE_BUTTON); | |
| 128 MAP_ROLE(ui::kAXProgressBarClassname, ui::AX_ROLE_PROGRESS_INDICATOR); | |
| 129 MAP_ROLE(ui::kAXRadioButtonClassname, ui::AX_ROLE_RADIO_BUTTON); | |
| 130 MAP_ROLE(ui::kAXSeekBarClassname, ui::AX_ROLE_SLIDER); | |
| 131 MAP_ROLE(ui::kAXSwitchClassname, ui::AX_ROLE_SWITCH); | |
| 132 MAP_ROLE(ui::kAXSpinnerClassname, ui::AX_ROLE_SPIN_BUTTON); | |
|
yawano
2017/02/28 05:36:07
question: We have different mapping for AX_ROLE_SP
David Tseng
2017/02/28 18:50:37
I think the more appropriate mapping isto a pop_up
yawano
2017/03/01 03:49:51
sgtm.
| |
| 133 MAP_ROLE(ui::kAXTabWidgetClassname, ui::AX_ROLE_TAB_LIST); | |
| 134 MAP_ROLE(ui::kAXToggleButtonClassname, ui::AX_ROLE_TOGGLE_BUTTON); | |
| 135 MAP_ROLE(ui::kAXViewClassname, ui::AX_ROLE_DIV); | |
| 136 MAP_ROLE(ui::kAXViewGroupClassname, ui::AX_ROLE_GROUP); | |
| 137 MAP_ROLE(ui::kAXWebViewClassname, ui::AX_ROLE_WEB_VIEW); | |
| 138 | |
| 139 #undef MAP_ROLE | |
| 140 | |
| 141 if (!text.empty()) | |
|
dmazzoni
2017/02/28 04:31:25
How about only if it's a leaf node. The rest of th
David Tseng
2017/02/28 18:50:37
This is the fallback case (in case we can't assign
| |
| 142 return ui::AX_ROLE_STATIC_TEXT; | |
| 143 else | |
| 144 return ui::AX_ROLE_DIV; | |
| 145 } | |
| 146 | |
| 147 int32_t GetAXState(arc::mojom::AccessibilityNodeInfoData* node) { | |
| 148 int32_t state = 0; | |
| 149 | |
| 150 #define MAP_STATE(android_boolean_property, chrome_state) \ | |
| 151 if (GetBooleanProperty(node, android_boolean_property)) \ | |
| 152 state |= 1 << chrome_state; | |
|
yawano
2017/02/28 05:36:07
Can we change GetAxState to be passed AxNodeData a
David Tseng
2017/02/28 18:50:37
Done
| |
| 153 | |
| 154 using AXBooleanProperty = arc::mojom::AccessibilityBooleanProperty; | |
| 155 | |
| 156 MAP_STATE(AXBooleanProperty::CHECKED, ui::AX_STATE_CHECKED); | |
| 157 MAP_STATE(AXBooleanProperty::EDITABLE, ui::AX_STATE_EDITABLE); | |
| 158 MAP_STATE(AXBooleanProperty::FOCUSABLE, ui::AX_STATE_FOCUSABLE); | |
| 159 MAP_STATE(AXBooleanProperty::MULTI_LINE, ui::AX_STATE_MULTILINE); | |
| 160 MAP_STATE(AXBooleanProperty::PASSWORD, ui::AX_STATE_PROTECTED); | |
| 161 MAP_STATE(AXBooleanProperty::SELECTED, ui::AX_STATE_SELECTED); | |
| 162 | |
| 163 #undef MAP_STATE | |
| 164 | |
| 165 if (!GetBooleanProperty(node, AXBooleanProperty::ENABLED)) | |
| 166 state |= 1 << ui::AX_STATE_DISABLED; | |
| 167 | |
| 168 return state; | |
| 169 } | |
| 170 | |
| 87 } // namespace | 171 } // namespace |
| 88 | 172 |
| 89 namespace arc { | 173 namespace arc { |
| 90 | 174 |
| 91 AXTreeSourceArc::AXTreeSourceArc(int32_t id) | 175 AXTreeSourceArc::AXTreeSourceArc(int32_t id) |
| 92 : tree_id_(id), | 176 : tree_id_(id), |
| 93 current_tree_serializer_(new AXTreeArcSerializer(this)), | 177 current_tree_serializer_(new AXTreeArcSerializer(this)), |
| 94 root_id_(-1), | 178 root_id_(-1), |
| 95 focused_node_id_(-1) {} | 179 focused_node_id_(-1) {} |
| 96 | 180 |
| 97 AXTreeSourceArc::~AXTreeSourceArc() { | 181 AXTreeSourceArc::~AXTreeSourceArc() { |
| 98 Reset(); | 182 Reset(); |
| 99 } | 183 } |
| 100 | 184 |
| 101 void AXTreeSourceArc::NotifyAccessibilityEvent( | 185 void AXTreeSourceArc::NotifyAccessibilityEvent( |
| 102 mojom::AccessibilityEventData* event_data) { | 186 mojom::AccessibilityEventData* event_data) { |
| 103 tree_map_.clear(); | 187 tree_map_.clear(); |
| 104 parent_map_.clear(); | 188 parent_map_.clear(); |
| 105 root_id_ = -1; | 189 root_id_ = -1; |
| 106 focused_node_id_ = -1; | |
| 107 for (size_t i = 0; i < event_data->nodeData.size(); ++i) { | 190 for (size_t i = 0; i < event_data->nodeData.size(); ++i) { |
| 108 if (!event_data->nodeData[i]->intListProperties) | 191 if (!event_data->nodeData[i]->intListProperties) |
| 109 continue; | 192 continue; |
| 110 auto it = event_data->nodeData[i]->intListProperties->find( | 193 auto it = event_data->nodeData[i]->intListProperties->find( |
| 111 arc::mojom::AccessibilityIntListProperty::CHILD_NODE_IDS); | 194 arc::mojom::AccessibilityIntListProperty::CHILD_NODE_IDS); |
| 112 if (it != event_data->nodeData[i]->intListProperties->end()) { | 195 if (it != event_data->nodeData[i]->intListProperties->end()) { |
| 113 for (size_t j = 0; j < it->second.size(); ++j) | 196 for (size_t j = 0; j < it->second.size(); ++j) |
| 114 parent_map_[it->second[j]] = event_data->nodeData[i]->id; | 197 parent_map_[it->second[j]] = event_data->nodeData[i]->id; |
| 115 } | 198 } |
| 116 } | 199 } |
| 117 | 200 |
| 118 for (size_t i = 0; i < event_data->nodeData.size(); ++i) { | 201 for (size_t i = 0; i < event_data->nodeData.size(); ++i) { |
| 119 int32_t id = event_data->nodeData[i]->id; | 202 int32_t id = event_data->nodeData[i]->id; |
| 120 tree_map_[id] = event_data->nodeData[i].get(); | 203 tree_map_[id] = event_data->nodeData[i].get(); |
| 121 if (parent_map_.find(id) == parent_map_.end()) { | 204 if (parent_map_.find(id) == parent_map_.end()) { |
| 122 CHECK_EQ(-1, root_id_) << "Duplicated root"; | 205 CHECK_EQ(-1, root_id_) << "Duplicated root"; |
| 123 root_id_ = id; | 206 root_id_ = id; |
| 124 } | 207 } |
| 125 } | 208 } |
| 126 | 209 |
| 127 ExtensionMsg_AccessibilityEventParams params; | 210 ExtensionMsg_AccessibilityEventParams params; |
| 128 params.event_type = ToAXEvent(event_data->eventType); | 211 params.event_type = ToAXEvent(event_data->eventType); |
| 129 if (params.event_type == ui::AX_EVENT_FOCUS) | 212 if (params.event_type == ui::AX_EVENT_FOCUS) |
| 130 focused_node_id_ = params.id; | 213 focused_node_id_ = params.id; |
| 214 else if (params.event_type == ui::AX_EVENT_BLUR) | |
| 215 focused_node_id_ = -1; | |
| 131 params.tree_id = tree_id_; | 216 params.tree_id = tree_id_; |
| 132 params.id = event_data->sourceId; | 217 params.id = event_data->sourceId; |
| 133 | 218 |
| 134 current_tree_serializer_->SerializeChanges(GetFromId(event_data->sourceId), | 219 current_tree_serializer_->SerializeChanges(GetFromId(event_data->sourceId), |
| 135 ¶ms.update); | 220 ¶ms.update); |
| 136 | 221 |
| 137 extensions::AutomationEventRouter* router = | 222 extensions::AutomationEventRouter* router = |
| 138 extensions::AutomationEventRouter::GetInstance(); | 223 extensions::AutomationEventRouter::GetInstance(); |
| 139 router->DispatchAccessibilityEvent(params); | 224 router->DispatchAccessibilityEvent(params); |
| 140 } | 225 } |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 203 | 288 |
| 204 mojom::AccessibilityNodeInfoData* AXTreeSourceArc::GetNull() const { | 289 mojom::AccessibilityNodeInfoData* AXTreeSourceArc::GetNull() const { |
| 205 return nullptr; | 290 return nullptr; |
| 206 } | 291 } |
| 207 | 292 |
| 208 void AXTreeSourceArc::SerializeNode(mojom::AccessibilityNodeInfoData* node, | 293 void AXTreeSourceArc::SerializeNode(mojom::AccessibilityNodeInfoData* node, |
| 209 ui::AXNodeData* out_data) const { | 294 ui::AXNodeData* out_data) const { |
| 210 if (!node) | 295 if (!node) |
| 211 return; | 296 return; |
| 212 out_data->id = node->id; | 297 out_data->id = node->id; |
| 213 out_data->state = 0; | |
| 214 | 298 |
| 215 using AXStringProperty = arc::mojom::AccessibilityStringProperty; | 299 using AXStringProperty = arc::mojom::AccessibilityStringProperty; |
| 216 std::string text; | 300 std::string text; |
| 217 if (GetStringProperty(node, AXStringProperty::TEXT, &text)) | 301 if (GetStringProperty(node, AXStringProperty::TEXT, &text)) |
| 218 out_data->SetName(text); | 302 out_data->SetName(text); |
| 219 else if (GetStringProperty(node, AXStringProperty::CONTENT_DESCRIPTION, | 303 else if (GetStringProperty(node, AXStringProperty::CONTENT_DESCRIPTION, |
| 220 &text)) | 304 &text)) |
| 221 out_data->SetName(text); | 305 out_data->SetName(text); |
| 222 | 306 |
| 223 int32_t id = node->id; | 307 int32_t id = node->id; |
| 224 if (id == root_id_) | 308 if (id == root_id_) |
| 225 out_data->role = ui::AX_ROLE_ROOT_WEB_AREA; | 309 out_data->role = ui::AX_ROLE_ROOT_WEB_AREA; |
| 226 else if (!text.empty()) | |
| 227 out_data->role = ui::AX_ROLE_STATIC_TEXT; | |
| 228 else | 310 else |
| 229 out_data->role = ui::AX_ROLE_DIV; | 311 out_data->role = GetAXRole(node); |
| 312 | |
| 313 out_data->state = GetAXState(node); | |
| 230 | 314 |
| 231 const gfx::Rect bounds_in_screen = GetBounds(node); | 315 const gfx::Rect bounds_in_screen = GetBounds(node); |
| 232 out_data->location.SetRect(bounds_in_screen.x(), bounds_in_screen.y(), | 316 out_data->location.SetRect(bounds_in_screen.x(), bounds_in_screen.y(), |
| 233 bounds_in_screen.width(), | 317 bounds_in_screen.width(), |
| 234 bounds_in_screen.height()); | 318 bounds_in_screen.height()); |
| 319 | |
| 320 if (out_data->role == ui::AX_ROLE_TEXT_FIELD && !text.empty()) | |
| 321 out_data->AddStringAttribute(ui::AX_ATTR_VALUE, text); | |
| 235 } | 322 } |
| 236 | 323 |
| 237 void AXTreeSourceArc::Reset() { | 324 void AXTreeSourceArc::Reset() { |
| 238 tree_map_.clear(); | 325 tree_map_.clear(); |
| 239 parent_map_.clear(); | 326 parent_map_.clear(); |
| 240 current_tree_serializer_.reset(new AXTreeArcSerializer(this)); | 327 current_tree_serializer_.reset(new AXTreeArcSerializer(this)); |
| 241 root_id_ = -1; | 328 root_id_ = -1; |
| 242 focused_node_id_ = -1; | 329 focused_node_id_ = -1; |
| 243 extensions::AutomationEventRouter* router = | 330 extensions::AutomationEventRouter* router = |
| 244 extensions::AutomationEventRouter::GetInstance(); | 331 extensions::AutomationEventRouter::GetInstance(); |
| 245 router->DispatchTreeDestroyedEvent(tree_id_, nullptr); | 332 router->DispatchTreeDestroyedEvent(tree_id_, nullptr); |
| 246 } | 333 } |
| 247 | 334 |
| 248 } // namespace arc | 335 } // namespace arc |
| OLD | NEW |