IndexedDBFactory now ForceCloses databases.
[chromium-blink-merge.git] / content / browser / accessibility / browser_accessibility_manager_android.cc
blob017afd1eed537d6843a485f27a24ab317373c44d
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
3 // found in the LICENSE file.
5 #include "content/browser/accessibility/browser_accessibility_manager_android.h"
7 #include <cmath>
9 #include "base/android/jni_android.h"
10 #include "base/android/jni_string.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/values.h"
14 #include "content/browser/accessibility/browser_accessibility_android.h"
15 #include "content/common/accessibility_messages.h"
16 #include "jni/BrowserAccessibilityManager_jni.h"
18 using base::android::AttachCurrentThread;
19 using base::android::ScopedJavaLocalRef;
21 namespace {
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
29 // Restricts |val| to the range [min, max].
30 int Clamp(int val, int min, int max) {
31 return std::min(std::max(val, min), max);
34 } // anonymous namespace
36 namespace content {
38 namespace aria_strings {
39 const char kAriaLivePolite[] = "polite";
40 const char kAriaLiveAssertive[] = "assertive";
43 // static
44 BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
45 const ui::AXNodeData& src,
46 BrowserAccessibilityDelegate* delegate,
47 BrowserAccessibilityFactory* factory) {
48 return new BrowserAccessibilityManagerAndroid(ScopedJavaLocalRef<jobject>(),
49 src, delegate, factory);
52 BrowserAccessibilityManagerAndroid*
53 BrowserAccessibilityManager::ToBrowserAccessibilityManagerAndroid() {
54 return static_cast<BrowserAccessibilityManagerAndroid*>(this);
57 BrowserAccessibilityManagerAndroid::BrowserAccessibilityManagerAndroid(
58 ScopedJavaLocalRef<jobject> content_view_core,
59 const ui::AXNodeData& src,
60 BrowserAccessibilityDelegate* delegate,
61 BrowserAccessibilityFactory* factory)
62 : BrowserAccessibilityManager(src, delegate, factory) {
63 SetContentViewCore(content_view_core);
66 BrowserAccessibilityManagerAndroid::~BrowserAccessibilityManagerAndroid() {
67 JNIEnv* env = AttachCurrentThread();
68 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
69 if (obj.is_null())
70 return;
72 Java_BrowserAccessibilityManager_onNativeObjectDestroyed(env, obj.obj());
75 // static
76 ui::AXNodeData BrowserAccessibilityManagerAndroid::GetEmptyDocument() {
77 ui::AXNodeData empty_document;
78 empty_document.id = 0;
79 empty_document.role = ui::AX_ROLE_ROOT_WEB_AREA;
80 empty_document.state = 1 << ui::AX_STATE_READONLY;
81 return empty_document;
84 void BrowserAccessibilityManagerAndroid::SetContentViewCore(
85 ScopedJavaLocalRef<jobject> content_view_core) {
86 if (content_view_core.is_null())
87 return;
89 JNIEnv* env = AttachCurrentThread();
90 java_ref_ = JavaObjectWeakGlobalRef(
91 env, Java_BrowserAccessibilityManager_create(
92 env, reinterpret_cast<intptr_t>(this),
93 content_view_core.obj()).obj());
96 void BrowserAccessibilityManagerAndroid::NotifyAccessibilityEvent(
97 ui::AXEvent event_type,
98 BrowserAccessibility* node) {
99 JNIEnv* env = AttachCurrentThread();
100 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
101 if (obj.is_null())
102 return;
104 if (event_type == ui::AX_EVENT_HIDE)
105 return;
107 // Always send AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED to notify
108 // the Android system that the accessibility hierarchy rooted at this
109 // node has changed.
110 Java_BrowserAccessibilityManager_handleContentChanged(
111 env, obj.obj(), node->renderer_id());
113 switch (event_type) {
114 case ui::AX_EVENT_LOAD_COMPLETE:
115 Java_BrowserAccessibilityManager_handlePageLoaded(
116 env, obj.obj(), focus_->renderer_id());
117 break;
118 case ui::AX_EVENT_FOCUS:
119 Java_BrowserAccessibilityManager_handleFocusChanged(
120 env, obj.obj(), node->renderer_id());
121 break;
122 case ui::AX_EVENT_CHECKED_STATE_CHANGED:
123 Java_BrowserAccessibilityManager_handleCheckStateChanged(
124 env, obj.obj(), node->renderer_id());
125 break;
126 case ui::AX_EVENT_SCROLLED_TO_ANCHOR:
127 Java_BrowserAccessibilityManager_handleScrolledToAnchor(
128 env, obj.obj(), node->renderer_id());
129 break;
130 case ui::AX_EVENT_ALERT:
131 // An alert is a special case of live region. Fall through to the
132 // next case to handle it.
133 case ui::AX_EVENT_SHOW: {
134 // This event is fired when an object appears in a live region.
135 // Speak its text.
136 BrowserAccessibilityAndroid* android_node =
137 static_cast<BrowserAccessibilityAndroid*>(node);
138 Java_BrowserAccessibilityManager_announceLiveRegionText(
139 env, obj.obj(),
140 base::android::ConvertUTF16ToJavaString(
141 env, android_node->GetText()).obj());
142 break;
144 case ui::AX_EVENT_SELECTED_TEXT_CHANGED:
145 Java_BrowserAccessibilityManager_handleTextSelectionChanged(
146 env, obj.obj(), node->renderer_id());
147 break;
148 case ui::AX_EVENT_CHILDREN_CHANGED:
149 case ui::AX_EVENT_TEXT_CHANGED:
150 case ui::AX_EVENT_VALUE_CHANGED:
151 if (node->IsEditableText()) {
152 Java_BrowserAccessibilityManager_handleEditableTextChanged(
153 env, obj.obj(), node->renderer_id());
155 break;
156 default:
157 // There are some notifications that aren't meaningful on Android.
158 // It's okay to skip them.
159 break;
163 jint BrowserAccessibilityManagerAndroid::GetRootId(JNIEnv* env, jobject obj) {
164 return static_cast<jint>(root_->renderer_id());
167 jboolean BrowserAccessibilityManagerAndroid::IsNodeValid(
168 JNIEnv* env, jobject obj, jint id) {
169 return GetFromRendererID(id) != NULL;
172 jint BrowserAccessibilityManagerAndroid::HitTest(
173 JNIEnv* env, jobject obj, jint x, jint y) {
174 BrowserAccessibilityAndroid* result =
175 static_cast<BrowserAccessibilityAndroid*>(
176 root_->BrowserAccessibilityForPoint(gfx::Point(x, y)));
178 if (!result)
179 return root_->renderer_id();
181 if (result->IsFocusable())
182 return result->renderer_id();
184 // Examine the children of |result| to find the nearest accessibility focus
185 // candidate
186 BrowserAccessibility* nearest_node = FuzzyHitTest(x, y, result);
187 if (nearest_node)
188 return nearest_node->renderer_id();
190 return root_->renderer_id();
193 jboolean BrowserAccessibilityManagerAndroid::PopulateAccessibilityNodeInfo(
194 JNIEnv* env, jobject obj, jobject info, jint id) {
195 BrowserAccessibilityAndroid* node = static_cast<BrowserAccessibilityAndroid*>(
196 GetFromRendererID(id));
197 if (!node)
198 return false;
200 if (node->parent()) {
201 Java_BrowserAccessibilityManager_setAccessibilityNodeInfoParent(
202 env, obj, info, node->parent()->renderer_id());
204 for (unsigned i = 0; i < node->PlatformChildCount(); ++i) {
205 Java_BrowserAccessibilityManager_addAccessibilityNodeInfoChild(
206 env, obj, info, node->children()[i]->renderer_id());
208 Java_BrowserAccessibilityManager_setAccessibilityNodeInfoBooleanAttributes(
209 env, obj, info,
211 node->IsCheckable(),
212 node->IsChecked(),
213 node->IsClickable(),
214 node->IsEnabled(),
215 node->IsFocusable(),
216 node->IsFocused(),
217 node->IsPassword(),
218 node->IsScrollable(),
219 node->IsSelected(),
220 node->IsVisibleToUser());
221 Java_BrowserAccessibilityManager_setAccessibilityNodeInfoStringAttributes(
222 env, obj, info,
223 base::android::ConvertUTF8ToJavaString(env, node->GetClassName()).obj(),
224 base::android::ConvertUTF16ToJavaString(env, node->GetText()).obj());
226 gfx::Rect absolute_rect = node->GetLocalBoundsRect();
227 gfx::Rect parent_relative_rect = absolute_rect;
228 if (node->parent()) {
229 gfx::Rect parent_rect = node->parent()->GetLocalBoundsRect();
230 parent_relative_rect.Offset(-parent_rect.OffsetFromOrigin());
232 bool is_root = node->parent() == NULL;
233 Java_BrowserAccessibilityManager_setAccessibilityNodeInfoLocation(
234 env, obj, info,
235 absolute_rect.x(), absolute_rect.y(),
236 parent_relative_rect.x(), parent_relative_rect.y(),
237 absolute_rect.width(), absolute_rect.height(),
238 is_root);
240 // New KitKat APIs
241 Java_BrowserAccessibilityManager_setAccessibilityNodeInfoKitKatAttributes(
242 env, obj, info,
243 node->CanOpenPopup(),
244 node->IsContentInvalid(),
245 node->IsDismissable(),
246 node->IsMultiLine(),
247 node->AndroidInputType(),
248 node->AndroidLiveRegionType());
249 if (node->IsCollection()) {
250 Java_BrowserAccessibilityManager_setAccessibilityNodeInfoCollectionInfo(
251 env, obj, info,
252 node->RowCount(),
253 node->ColumnCount(),
254 node->IsHierarchical());
256 if (node->IsCollectionItem() || node->IsHeading()) {
257 Java_BrowserAccessibilityManager_setAccessibilityNodeInfoCollectionItemInfo(
258 env, obj, info,
259 node->RowIndex(),
260 node->RowSpan(),
261 node->ColumnIndex(),
262 node->ColumnSpan(),
263 node->IsHeading());
265 if (node->IsRangeType()) {
266 Java_BrowserAccessibilityManager_setAccessibilityNodeInfoRangeInfo(
267 env, obj, info,
268 node->AndroidRangeType(),
269 node->RangeMin(),
270 node->RangeMax(),
271 node->RangeCurrentValue());
274 return true;
277 jboolean BrowserAccessibilityManagerAndroid::PopulateAccessibilityEvent(
278 JNIEnv* env, jobject obj, jobject event, jint id, jint event_type) {
279 BrowserAccessibilityAndroid* node = static_cast<BrowserAccessibilityAndroid*>(
280 GetFromRendererID(id));
281 if (!node)
282 return false;
284 Java_BrowserAccessibilityManager_setAccessibilityEventBooleanAttributes(
285 env, obj, event,
286 node->IsChecked(),
287 node->IsEnabled(),
288 node->IsPassword(),
289 node->IsScrollable());
290 Java_BrowserAccessibilityManager_setAccessibilityEventClassName(
291 env, obj, event,
292 base::android::ConvertUTF8ToJavaString(env, node->GetClassName()).obj());
293 Java_BrowserAccessibilityManager_setAccessibilityEventListAttributes(
294 env, obj, event,
295 node->GetItemIndex(),
296 node->GetItemCount());
297 Java_BrowserAccessibilityManager_setAccessibilityEventScrollAttributes(
298 env, obj, event,
299 node->GetScrollX(),
300 node->GetScrollY(),
301 node->GetMaxScrollX(),
302 node->GetMaxScrollY());
304 switch (event_type) {
305 case ANDROID_ACCESSIBILITY_EVENT_TYPE_VIEW_TEXT_CHANGED:
306 Java_BrowserAccessibilityManager_setAccessibilityEventTextChangedAttrs(
307 env, obj, event,
308 node->GetTextChangeFromIndex(),
309 node->GetTextChangeAddedCount(),
310 node->GetTextChangeRemovedCount(),
311 base::android::ConvertUTF16ToJavaString(
312 env, node->GetTextChangeBeforeText()).obj(),
313 base::android::ConvertUTF16ToJavaString(env, node->GetText()).obj());
314 break;
315 case ANDROID_ACCESSIBILITY_EVENT_TYPE_VIEW_TEXT_SELECTION_CHANGED:
316 Java_BrowserAccessibilityManager_setAccessibilityEventSelectionAttrs(
317 env, obj, event,
318 node->GetSelectionStart(),
319 node->GetSelectionEnd(),
320 node->GetEditableTextLength(),
321 base::android::ConvertUTF16ToJavaString(env, node->GetText()).obj());
322 break;
323 default:
324 break;
327 // Backwards-compatible fallback for new KitKat APIs.
328 Java_BrowserAccessibilityManager_setAccessibilityEventKitKatAttributes(
329 env, obj, event,
330 node->CanOpenPopup(),
331 node->IsContentInvalid(),
332 node->IsDismissable(),
333 node->IsMultiLine(),
334 node->AndroidInputType(),
335 node->AndroidLiveRegionType());
336 if (node->IsCollection()) {
337 Java_BrowserAccessibilityManager_setAccessibilityEventCollectionInfo(
338 env, obj, event,
339 node->RowCount(),
340 node->ColumnCount(),
341 node->IsHierarchical());
343 if (node->IsCollectionItem() || node->IsHeading()) {
344 Java_BrowserAccessibilityManager_setAccessibilityEventCollectionItemInfo(
345 env, obj, event,
346 node->RowIndex(),
347 node->RowSpan(),
348 node->ColumnIndex(),
349 node->ColumnSpan(),
350 node->IsHeading());
352 if (node->IsRangeType()) {
353 Java_BrowserAccessibilityManager_setAccessibilityEventRangeInfo(
354 env, obj, event,
355 node->AndroidRangeType(),
356 node->RangeMin(),
357 node->RangeMax(),
358 node->RangeCurrentValue());
361 return true;
364 void BrowserAccessibilityManagerAndroid::Click(
365 JNIEnv* env, jobject obj, jint id) {
366 BrowserAccessibility* node = GetFromRendererID(id);
367 if (node)
368 DoDefaultAction(*node);
371 void BrowserAccessibilityManagerAndroid::Focus(
372 JNIEnv* env, jobject obj, jint id) {
373 BrowserAccessibility* node = GetFromRendererID(id);
374 if (node)
375 SetFocus(node, true);
378 void BrowserAccessibilityManagerAndroid::Blur(JNIEnv* env, jobject obj) {
379 SetFocus(root_, true);
382 BrowserAccessibility* BrowserAccessibilityManagerAndroid::FuzzyHitTest(
383 int x, int y, BrowserAccessibility* start_node) {
384 BrowserAccessibility* nearest_node = NULL;
385 int min_distance = INT_MAX;
386 FuzzyHitTestImpl(x, y, start_node, &nearest_node, &min_distance);
387 return nearest_node;
390 // static
391 void BrowserAccessibilityManagerAndroid::FuzzyHitTestImpl(
392 int x, int y, BrowserAccessibility* start_node,
393 BrowserAccessibility** nearest_candidate, int* nearest_distance) {
394 BrowserAccessibilityAndroid* node =
395 static_cast<BrowserAccessibilityAndroid*>(start_node);
396 int distance = CalculateDistanceSquared(x, y, node);
398 if (node->IsFocusable()) {
399 if (distance < *nearest_distance) {
400 *nearest_candidate = node;
401 *nearest_distance = distance;
403 // Don't examine any more children of focusable node
404 // TODO(aboxhall): what about focusable children?
405 return;
408 if (!node->GetText().empty()) {
409 if (distance < *nearest_distance) {
410 *nearest_candidate = node;
411 *nearest_distance = distance;
413 return;
416 for (uint32 i = 0; i < node->PlatformChildCount(); i++) {
417 BrowserAccessibility* child = node->PlatformGetChild(i);
418 FuzzyHitTestImpl(x, y, child, nearest_candidate, nearest_distance);
422 // static
423 int BrowserAccessibilityManagerAndroid::CalculateDistanceSquared(
424 int x, int y, BrowserAccessibility* node) {
425 gfx::Rect node_bounds = node->GetLocalBoundsRect();
426 int nearest_x = Clamp(x, node_bounds.x(), node_bounds.right());
427 int nearest_y = Clamp(y, node_bounds.y(), node_bounds.bottom());
428 int dx = std::abs(x - nearest_x);
429 int dy = std::abs(y - nearest_y);
430 return dx * dx + dy * dy;
433 void BrowserAccessibilityManagerAndroid::NotifyRootChanged() {
434 JNIEnv* env = AttachCurrentThread();
435 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
436 if (obj.is_null())
437 return;
439 Java_BrowserAccessibilityManager_handleNavigate(env, obj.obj());
442 bool
443 BrowserAccessibilityManagerAndroid::UseRootScrollOffsetsWhenComputingBounds() {
444 // The Java layer handles the root scroll offset.
445 return false;
448 bool RegisterBrowserAccessibilityManager(JNIEnv* env) {
449 return RegisterNativesImpl(env);
452 } // namespace content