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

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

Powered by Google App Engine
This is Rietveld 408576698