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

Side by Side Diff: content/browser/accessibility/browser_accessibility_manager_android.cc

Issue 15741009: Native Android accessibility. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Now build objs from C++ code using Java helpers Created 7 years, 6 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
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 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 "content/browser/accessibility/browser_accessibility_manager_android.h" 5 #include "content/browser/accessibility/browser_accessibility_manager_android.h"
6 6
7 #include <cmath> 7 #include <cmath>
8 8
9 #include "base/android/jni_android.h" 9 #include "base/android/jni_android.h"
10 #include "base/android/jni_string.h" 10 #include "base/android/jni_string.h"
11 #include "base/strings/string_number_conversions.h" 11 #include "base/strings/string_number_conversions.h"
12 #include "base/utf_string_conversions.h" 12 #include "base/utf_string_conversions.h"
13 #include "base/values.h" 13 #include "base/values.h"
14 #include "content/browser/accessibility/browser_accessibility_android.h" 14 #include "content/browser/accessibility/browser_accessibility_android.h"
15 #include "content/common/accessibility_messages.h" 15 #include "content/common/accessibility_messages.h"
16 #include "jni/BrowserAccessibilityManager_jni.h"
16 17
17 using base::android::AttachCurrentThread; 18 using base::android::AttachCurrentThread;
18 using base::android::ScopedJavaLocalRef; 19 using base::android::ScopedJavaLocalRef;
19 20
20 namespace { 21 namespace {
21 22
23 enum {
24 ANDROID_ACCESSIBILITY_EVENT_VIEW_TEXT_CHANGED = 16,
bulach 2013/06/20 09:18:42 where are these values coming from? maybe a commen
dmazzoni 2013/06/21 05:38:55 These are enums from android.view.accessibility.Ac
25 ANDROID_ACCESSIBILITY_EVENT_VIEW_TEXT_SELECTION_CHANGED = 8192
26 };
27
22 // Restricts |val| to the range [min, max]. 28 // Restricts |val| to the range [min, max].
23 int Clamp(int val, int min, int max) { 29 int Clamp(int val, int min, int max) {
24 return std::min(std::max(val, min), max); 30 return std::min(std::max(val, min), max);
25 } 31 }
26 32
27 } // anonymous namespace 33 } // anonymous namespace
28 34
29 namespace content { 35 namespace content {
30 36
31 namespace aria_strings { 37 namespace aria_strings {
(...skipping 12 matching lines...) Expand all
44 50
45 BrowserAccessibilityManagerAndroid::BrowserAccessibilityManagerAndroid( 51 BrowserAccessibilityManagerAndroid::BrowserAccessibilityManagerAndroid(
46 ScopedJavaLocalRef<jobject> content_view_core, 52 ScopedJavaLocalRef<jobject> content_view_core,
47 const AccessibilityNodeData& src, 53 const AccessibilityNodeData& src,
48 BrowserAccessibilityDelegate* delegate, 54 BrowserAccessibilityDelegate* delegate,
49 BrowserAccessibilityFactory* factory) 55 BrowserAccessibilityFactory* factory)
50 : BrowserAccessibilityManager(src, delegate, factory) { 56 : BrowserAccessibilityManager(src, delegate, factory) {
51 if (content_view_core.is_null()) 57 if (content_view_core.is_null())
52 return; 58 return;
53 59
54 // TODO(aboxhall): set up Java references 60 JNIEnv* env = AttachCurrentThread();
61 java_ref_ = JavaObjectWeakGlobalRef(
62 env, Java_BrowserAccessibilityManager_create(
63 env, reinterpret_cast<jint>(this), content_view_core.obj()).obj());
55 } 64 }
56 65
57 BrowserAccessibilityManagerAndroid::~BrowserAccessibilityManagerAndroid() { 66 BrowserAccessibilityManagerAndroid::~BrowserAccessibilityManagerAndroid() {
58 JNIEnv* env = base::android::AttachCurrentThread(); 67 JNIEnv* env = AttachCurrentThread();
59 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); 68 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
60 if (obj.is_null()) 69 if (obj.is_null())
61 return; 70 return;
62 71
63 // TODO(aboxhall): tear down Java references 72 Java_BrowserAccessibilityManager_onNativeObjectDestroyed(env, obj.obj());
64 } 73 }
65 74
66 // static 75 // static
67 AccessibilityNodeData BrowserAccessibilityManagerAndroid::GetEmptyDocument() { 76 AccessibilityNodeData BrowserAccessibilityManagerAndroid::GetEmptyDocument() {
68 AccessibilityNodeData empty_document; 77 AccessibilityNodeData empty_document;
69 empty_document.id = 0; 78 empty_document.id = 0;
70 empty_document.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA; 79 empty_document.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
71 empty_document.state = 1 << AccessibilityNodeData::STATE_READONLY; 80 empty_document.state = 1 << AccessibilityNodeData::STATE_READONLY;
72 return empty_document; 81 return empty_document;
73 } 82 }
74 83
75 void BrowserAccessibilityManagerAndroid::NotifyAccessibilityEvent( 84 void BrowserAccessibilityManagerAndroid::NotifyAccessibilityEvent(
76 int type, 85 int type,
77 BrowserAccessibility* node) { 86 BrowserAccessibility* node) {
78 JNIEnv* env = base::android::AttachCurrentThread(); 87 JNIEnv* env = AttachCurrentThread();
79 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); 88 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
80 if (obj.is_null()) 89 if (obj.is_null())
81 return; 90 return;
82 91
83 // TODO(aboxhall): call into appropriate Java method for each type of 92 switch (type) {
84 // notification 93 case AccessibilityNotificationLoadComplete:
94 Java_BrowserAccessibilityManager_handlePageLoaded(
95 env, obj.obj(), focus_->renderer_id());
96 break;
97 case AccessibilityNotificationFocusChanged:
98 Java_BrowserAccessibilityManager_handleFocusChanged(
99 env, obj.obj(), node->renderer_id());
100 break;
101 case AccessibilityNotificationCheckStateChanged:
102 Java_BrowserAccessibilityManager_handleCheckStateChanged(
103 env, obj.obj(), node->renderer_id());
104 break;
105 case AccessibilityNotificationScrolledToAnchor:
106 Java_BrowserAccessibilityManager_handleScrolledToAnchor(
107 env, obj.obj(), node->renderer_id());
108 break;
109 case AccessibilityNotificationAlert:
110 // An alert is a special case of live region. Fall through to the
111 // next case to handle it.
112 case AccessibilityNotificationObjectShow: {
113 // This event is fired when an object appears in a live region.
114 // Speak its text.
115 BrowserAccessibilityAndroid* android_node =
116 static_cast<BrowserAccessibilityAndroid*>(node);
117 Java_BrowserAccessibilityManager_announceLiveRegionText(
118 env, obj.obj(),
119 base::android::ConvertUTF16ToJavaString(
120 env, android_node->GetText()).obj());
121 break;
122 }
123 case AccessibilityNotificationSelectedTextChanged:
124 Java_BrowserAccessibilityManager_handleTextSelectionChanged(
125 env, obj.obj(), node->renderer_id());
126 break;
127 case AccessibilityNotificationChildrenChanged:
128 case AccessibilityNotificationTextChanged:
129 case AccessibilityNotificationValueChanged:
130 if (node->IsEditableText()) {
131 Java_BrowserAccessibilityManager_handleEditableTextChanged(
132 env, obj.obj(), node->renderer_id());
133 } else {
134 Java_BrowserAccessibilityManager_handleContentChanged(
135 env, obj.obj(), node->renderer_id());
136 }
137 break;
138 default:
139 // There are some notifications that aren't meaningful on Android.
140 // It's okay to skip them.
141 break;
142 }
85 } 143 }
86 144
87 jint BrowserAccessibilityManagerAndroid::GetRootId(JNIEnv* env, jobject obj) { 145 jint BrowserAccessibilityManagerAndroid::GetRootId(JNIEnv* env, jobject obj) {
88 return static_cast<jint>(root_->renderer_id()); 146 return static_cast<jint>(root_->renderer_id());
89 } 147 }
90 148
91 jint BrowserAccessibilityManagerAndroid::HitTest( 149 jint BrowserAccessibilityManagerAndroid::HitTest(
92 JNIEnv* env, jobject obj, jint x, jint y) { 150 JNIEnv* env, jobject obj, jint x, jint y) {
93 BrowserAccessibilityAndroid* result = 151 BrowserAccessibilityAndroid* result =
94 static_cast<BrowserAccessibilityAndroid*>( 152 static_cast<BrowserAccessibilityAndroid*>(
95 root_->BrowserAccessibilityForPoint(gfx::Point(x, y))); 153 root_->BrowserAccessibilityForPoint(gfx::Point(x, y)));
96 154
97 if (!result) 155 if (!result)
98 return root_->renderer_id(); 156 return root_->renderer_id();
99 157
100 if (result->IsFocusable()) 158 if (result->IsFocusable())
101 return result->renderer_id(); 159 return result->renderer_id();
102 160
103 // Examine the children of |result| to find the nearest accessibility focus 161 // Examine the children of |result| to find the nearest accessibility focus
104 // candidate 162 // candidate
105 BrowserAccessibility* nearest_node = FuzzyHitTest(x, y, result); 163 BrowserAccessibility* nearest_node = FuzzyHitTest(x, y, result);
106 if (nearest_node) 164 if (nearest_node)
107 return nearest_node->renderer_id(); 165 return nearest_node->renderer_id();
108 166
109 return root_->renderer_id(); 167 return root_->renderer_id();
110 } 168 }
111 169
170 jboolean BrowserAccessibilityManagerAndroid::PopulateAccessibilityNodeInfo(
171 JNIEnv* env, jobject obj, jobject info, jint id) {
172 BrowserAccessibilityAndroid* node = static_cast<BrowserAccessibilityAndroid*>(
173 GetFromRendererID(id));
174 if (!node)
175 return false;
176
177 if (node->parent()) {
178 Java_BrowserAccessibilityManager_setAccessibilityNodeInfoParent(
179 env, obj, info, node->parent()->renderer_id());
180 }
181 if (!node->IsLeaf()) {
182 for (unsigned i = 0; i < node->child_count(); ++i) {
183 Java_BrowserAccessibilityManager_addAccessibilityNodeInfoChild(
184 env, obj, info, node->children()[i]->renderer_id());
185 }
186 }
187 Java_BrowserAccessibilityManager_setAccessibilityNodeInfoBooleanAttributes(
188 env, obj, info,
189 node->IsCheckable(),
190 node->IsChecked(),
191 node->IsClickable(),
192 node->IsEnabled(),
193 node->IsFocusable(),
194 node->IsFocused(),
195 node->IsPassword(),
196 node->IsScrollable(),
197 node->IsSelected(),
198 node->IsVisibleToUser());
199 Java_BrowserAccessibilityManager_setAccessibilityNodeInfoStringAttributes(
200 env, obj, info,
201 base::android::ConvertUTF8ToJavaString(env, node->GetClassName()).obj(),
202 base::android::ConvertUTF16ToJavaString(env, node->GetText()).obj());
203
204 gfx::Rect absolute_rect = node->GetLocalBoundsRect();
205 gfx::Rect parent_relative_rect = absolute_rect;
206 if (node->parent()) {
207 gfx::Rect parent_rect = node->parent()->GetLocalBoundsRect();
208 parent_relative_rect.Offset(-parent_rect.OffsetFromOrigin());
209 }
210 bool is_root = node->parent() == NULL;
211 Java_BrowserAccessibilityManager_setAccessibilityNodeInfoLocation(
212 env, obj, info,
213 absolute_rect.x(), absolute_rect.y(),
214 parent_relative_rect.x(), parent_relative_rect.y(),
215 absolute_rect.width(), absolute_rect.height(),
216 is_root);
217
218 return true;
219 }
220
221 jboolean BrowserAccessibilityManagerAndroid::PopulateAccessibilityEvent(
222 JNIEnv* env, jobject obj, jobject event, jint id, jint event_type) {
223 BrowserAccessibilityAndroid* node = static_cast<BrowserAccessibilityAndroid*>(
224 GetFromRendererID(id));
225 if (!node)
226 return false;
227
228 Java_BrowserAccessibilityManager_setAccessibilityEventBooleanAttributes(
229 env, obj, event,
230 node->IsChecked(),
231 node->IsEnabled(),
232 node->IsPassword(),
233 node->IsScrollable());
234 Java_BrowserAccessibilityManager_setAccessibilityEventClassName(
235 env, obj, event,
236 base::android::ConvertUTF8ToJavaString(env, node->GetClassName()).obj());
237 Java_BrowserAccessibilityManager_setAccessibilityEventListAttributes(
238 env, obj, event,
239 node->GetItemIndex(),
240 node->GetItemCount());
241 Java_BrowserAccessibilityManager_setAccessibilityEventScrollAttributes(
242 env, obj, event,
243 node->GetScrollX(),
244 node->GetScrollY(),
245 node->GetMaxScrollX(),
246 node->GetMaxScrollY());
247
248 switch (event_type) {
249 case ANDROID_ACCESSIBILITY_EVENT_VIEW_TEXT_CHANGED:
250 Java_BrowserAccessibilityManager_setAccessibilityEventTextChangedAttrs(
251 env, obj, event,
252 node->GetTextChangeFromIndex(),
253 node->GetTextChangeAddedCount(),
254 node->GetTextChangeRemovedCount(),
255 base::android::ConvertUTF16ToJavaString(
256 env, node->GetTextChangeBeforeText()).obj(),
257 base::android::ConvertUTF16ToJavaString(env, node->GetText()).obj());
258 break;
259 case ANDROID_ACCESSIBILITY_EVENT_VIEW_TEXT_SELECTION_CHANGED:
260 Java_BrowserAccessibilityManager_setAccessibilityEventSelectionAttrs(
261 env, obj, event,
262 node->GetSelectionStart(),
263 node->GetSelectionEnd(),
264 node->GetEditableTextLength(),
265 base::android::ConvertUTF16ToJavaString(env, node->GetText()).obj());
266 break;
267 default:
268 break;
269 }
270
271 return true;
272 }
273
274 void BrowserAccessibilityManagerAndroid::Click(
275 JNIEnv* env, jobject obj, jint id) {
276 BrowserAccessibility* node = GetFromRendererID(id);
277 if (node)
278 DoDefaultAction(*node);
279 }
280
281 void BrowserAccessibilityManagerAndroid::Focus(
282 JNIEnv* env, jobject obj, jint id) {
283 BrowserAccessibility* node = GetFromRendererID(id);
284 if (node)
285 SetFocus(node, true);
286 }
287
112 BrowserAccessibility* BrowserAccessibilityManagerAndroid::FuzzyHitTest( 288 BrowserAccessibility* BrowserAccessibilityManagerAndroid::FuzzyHitTest(
113 int x, int y, BrowserAccessibility* start_node) { 289 int x, int y, BrowserAccessibility* start_node) {
114 BrowserAccessibility* nearest_node = NULL; 290 BrowserAccessibility* nearest_node = NULL;
115 int min_distance = INT_MAX; 291 int min_distance = INT_MAX;
116 FuzzyHitTestImpl(x, y, start_node, &nearest_node, &min_distance); 292 FuzzyHitTestImpl(x, y, start_node, &nearest_node, &min_distance);
117 return nearest_node; 293 return nearest_node;
118 } 294 }
119 295
120 // static 296 // static
121 void BrowserAccessibilityManagerAndroid::FuzzyHitTestImpl( 297 void BrowserAccessibilityManagerAndroid::FuzzyHitTestImpl(
122 int x, int y, BrowserAccessibility* start_node, 298 int x, int y, BrowserAccessibility* start_node,
123 BrowserAccessibility** nearest_candidate, int* nearest_distance) { 299 BrowserAccessibility** nearest_candidate, int* nearest_distance) {
124 BrowserAccessibilityAndroid* node = 300 BrowserAccessibilityAndroid* node =
125 static_cast<BrowserAccessibilityAndroid*>(start_node); 301 static_cast<BrowserAccessibilityAndroid*>(start_node);
126 int distance = CalculateDistanceSquared(x, y, node); 302 int distance = CalculateDistanceSquared(x, y, node);
127 303
128 if (node->IsFocusable()) { 304 if (node->IsFocusable()) {
129 if (distance < *nearest_distance) { 305 if (distance < *nearest_distance) {
130 *nearest_candidate = node; 306 *nearest_candidate = node;
131 *nearest_distance = distance; 307 *nearest_distance = distance;
132 } 308 }
133 // Don't examine any more children of focusable node 309 // Don't examine any more children of focusable node
134 // TODO(aboxhall): what about focusable children? 310 // TODO(aboxhall): what about focusable children?
135 return; 311 return;
136 } 312 }
137 313
138 if (!node->ComputeName().empty()) { 314 if (!node->GetText().empty()) {
139 if (distance < *nearest_distance) { 315 if (distance < *nearest_distance) {
140 *nearest_candidate = node; 316 *nearest_candidate = node;
141 *nearest_distance = distance; 317 *nearest_distance = distance;
142 } 318 }
143 return; 319 return;
144 } 320 }
145 321
146 if (!node->IsLeaf()) { 322 if (!node->IsLeaf()) {
147 for (uint32 i = 0; i < node->child_count(); i++) { 323 for (uint32 i = 0; i < node->child_count(); i++) {
148 BrowserAccessibility* child = node->GetChild(i); 324 BrowserAccessibility* child = node->GetChild(i);
149 FuzzyHitTestImpl(x, y, child, nearest_candidate, nearest_distance); 325 FuzzyHitTestImpl(x, y, child, nearest_candidate, nearest_distance);
150 } 326 }
151 } 327 }
152 } 328 }
153 329
154 // static 330 // static
155 int BrowserAccessibilityManagerAndroid::CalculateDistanceSquared( 331 int BrowserAccessibilityManagerAndroid::CalculateDistanceSquared(
156 int x, int y, BrowserAccessibility* node) { 332 int x, int y, BrowserAccessibility* node) {
157 gfx::Rect node_bounds = node->GetLocalBoundsRect(); 333 gfx::Rect node_bounds = node->GetLocalBoundsRect();
158 int nearest_x = Clamp(x, node_bounds.x(), node_bounds.right()); 334 int nearest_x = Clamp(x, node_bounds.x(), node_bounds.right());
159 int nearest_y = Clamp(y, node_bounds.y(), node_bounds.bottom()); 335 int nearest_y = Clamp(y, node_bounds.y(), node_bounds.bottom());
160 int dx = std::abs(x - nearest_x); 336 int dx = std::abs(x - nearest_x);
161 int dy = std::abs(y - nearest_y); 337 int dy = std::abs(y - nearest_y);
162 return dx * dx + dy * dy; 338 return dx * dx + dy * dy;
163 } 339 }
164 340
165 jint BrowserAccessibilityManagerAndroid::GetNativeNodeById( 341 void BrowserAccessibilityManagerAndroid::NotifyRootChanged() {
166 JNIEnv* env, jobject obj, jint id) { 342 JNIEnv* env = AttachCurrentThread();
167 return reinterpret_cast<jint>(GetFromRendererID(id)); 343 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
168 } 344 if (obj.is_null())
345 return;
169 346
170 void BrowserAccessibilityManagerAndroid::NotifyRootChanged() { 347 Java_BrowserAccessibilityManager_handleNavigate(env, obj.obj());
171 // TODO(aboxhall): non-stub implementation
172 } 348 }
173 349
174 bool 350 bool
175 BrowserAccessibilityManagerAndroid::UseRootScrollOffsetsWhenComputingBounds() { 351 BrowserAccessibilityManagerAndroid::UseRootScrollOffsetsWhenComputingBounds() {
176 // The Java layer handles the root scroll offset. 352 // The Java layer handles the root scroll offset.
177 return false; 353 return false;
178 } 354 }
179 355
180 bool RegisterBrowserAccessibilityManager(JNIEnv* env) { 356 bool RegisterBrowserAccessibilityManager(JNIEnv* env) {
181 // TODO(aboxhall): non-stub implementation 357 return RegisterNativesImpl(env);
182 return false;
183 } 358 }
184 359
185 } // namespace content 360 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698