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

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

Powered by Google App Engine
This is Rietveld 408576698