Index: content/browser/accessibility/browser_accessibility_manager_android.cc |
diff --git a/content/browser/accessibility/browser_accessibility_manager_android.cc b/content/browser/accessibility/browser_accessibility_manager_android.cc |
index 861375cb14bfec79e98ecf05c18e7ee6c36857c3..e14aa0bb977b44f43e647f347a82f17dc67a815c 100644 |
--- a/content/browser/accessibility/browser_accessibility_manager_android.cc |
+++ b/content/browser/accessibility/browser_accessibility_manager_android.cc |
@@ -13,12 +13,19 @@ |
#include "base/values.h" |
#include "content/browser/accessibility/browser_accessibility_android.h" |
#include "content/common/accessibility_messages.h" |
+#include "jni/BrowserAccessibilityManager_jni.h" |
using base::android::AttachCurrentThread; |
using base::android::ScopedJavaLocalRef; |
namespace { |
+// These are enums from android.view.accessibility.AccessibilityEvent in Java: |
+enum { |
+ ANDROID_ACCESSIBILITY_EVENT_TYPE_VIEW_TEXT_CHANGED = 16, |
+ ANDROID_ACCESSIBILITY_EVENT_TYPE_VIEW_TEXT_SELECTION_CHANGED = 8192 |
+}; |
+ |
// Restricts |val| to the range [min, max]. |
int Clamp(int val, int min, int max) { |
return std::min(std::max(val, min), max); |
@@ -51,16 +58,19 @@ BrowserAccessibilityManagerAndroid::BrowserAccessibilityManagerAndroid( |
if (content_view_core.is_null()) |
return; |
- // TODO(aboxhall): set up Java references |
+ JNIEnv* env = AttachCurrentThread(); |
+ java_ref_ = JavaObjectWeakGlobalRef( |
+ env, Java_BrowserAccessibilityManager_create( |
+ env, reinterpret_cast<jint>(this), content_view_core.obj()).obj()); |
} |
BrowserAccessibilityManagerAndroid::~BrowserAccessibilityManagerAndroid() { |
- JNIEnv* env = base::android::AttachCurrentThread(); |
+ JNIEnv* env = AttachCurrentThread(); |
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); |
if (obj.is_null()) |
return; |
- // TODO(aboxhall): tear down Java references |
+ Java_BrowserAccessibilityManager_onNativeObjectDestroyed(env, obj.obj()); |
} |
// static |
@@ -75,13 +85,62 @@ AccessibilityNodeData BrowserAccessibilityManagerAndroid::GetEmptyDocument() { |
void BrowserAccessibilityManagerAndroid::NotifyAccessibilityEvent( |
int type, |
BrowserAccessibility* node) { |
- JNIEnv* env = base::android::AttachCurrentThread(); |
+ JNIEnv* env = AttachCurrentThread(); |
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); |
if (obj.is_null()) |
return; |
- // TODO(aboxhall): call into appropriate Java method for each type of |
- // notification |
+ switch (type) { |
+ case AccessibilityNotificationLoadComplete: |
+ Java_BrowserAccessibilityManager_handlePageLoaded( |
+ env, obj.obj(), focus_->renderer_id()); |
+ break; |
+ case AccessibilityNotificationFocusChanged: |
+ Java_BrowserAccessibilityManager_handleFocusChanged( |
+ env, obj.obj(), node->renderer_id()); |
+ break; |
+ case AccessibilityNotificationCheckStateChanged: |
+ Java_BrowserAccessibilityManager_handleCheckStateChanged( |
+ env, obj.obj(), node->renderer_id()); |
+ break; |
+ case AccessibilityNotificationScrolledToAnchor: |
+ Java_BrowserAccessibilityManager_handleScrolledToAnchor( |
+ env, obj.obj(), node->renderer_id()); |
+ break; |
+ case AccessibilityNotificationAlert: |
+ // An alert is a special case of live region. Fall through to the |
+ // next case to handle it. |
+ case AccessibilityNotificationObjectShow: { |
+ // This event is fired when an object appears in a live region. |
+ // Speak its text. |
+ BrowserAccessibilityAndroid* android_node = |
+ static_cast<BrowserAccessibilityAndroid*>(node); |
+ Java_BrowserAccessibilityManager_announceLiveRegionText( |
+ env, obj.obj(), |
+ base::android::ConvertUTF16ToJavaString( |
+ env, android_node->GetText()).obj()); |
+ break; |
+ } |
+ case AccessibilityNotificationSelectedTextChanged: |
+ Java_BrowserAccessibilityManager_handleTextSelectionChanged( |
+ env, obj.obj(), node->renderer_id()); |
+ break; |
+ case AccessibilityNotificationChildrenChanged: |
+ case AccessibilityNotificationTextChanged: |
+ case AccessibilityNotificationValueChanged: |
+ if (node->IsEditableText()) { |
+ Java_BrowserAccessibilityManager_handleEditableTextChanged( |
+ env, obj.obj(), node->renderer_id()); |
+ } else { |
+ Java_BrowserAccessibilityManager_handleContentChanged( |
+ env, obj.obj(), node->renderer_id()); |
+ } |
+ break; |
+ default: |
+ // There are some notifications that aren't meaningful on Android. |
+ // It's okay to skip them. |
+ break; |
+ } |
} |
jint BrowserAccessibilityManagerAndroid::GetRootId(JNIEnv* env, jobject obj) { |
@@ -109,6 +168,129 @@ jint BrowserAccessibilityManagerAndroid::HitTest( |
return root_->renderer_id(); |
} |
+jboolean BrowserAccessibilityManagerAndroid::PopulateAccessibilityNodeInfo( |
+ JNIEnv* env, jobject obj, jobject info, jint id) { |
+ BrowserAccessibilityAndroid* node = static_cast<BrowserAccessibilityAndroid*>( |
+ GetFromRendererID(id)); |
+ if (!node) |
+ return false; |
+ |
+ if (node->parent()) { |
+ Java_BrowserAccessibilityManager_setAccessibilityNodeInfoParent( |
+ env, obj, info, node->parent()->renderer_id()); |
+ } |
+ if (!node->IsLeaf()) { |
+ for (unsigned i = 0; i < node->child_count(); ++i) { |
+ Java_BrowserAccessibilityManager_addAccessibilityNodeInfoChild( |
+ env, obj, info, node->children()[i]->renderer_id()); |
+ } |
+ } |
+ Java_BrowserAccessibilityManager_setAccessibilityNodeInfoBooleanAttributes( |
+ env, obj, info, |
+ id, |
+ node->IsCheckable(), |
+ node->IsChecked(), |
+ node->IsClickable(), |
+ node->IsEnabled(), |
+ node->IsFocusable(), |
+ node->IsFocused(), |
+ node->IsPassword(), |
+ node->IsScrollable(), |
+ node->IsSelected(), |
+ node->IsVisibleToUser()); |
+ Java_BrowserAccessibilityManager_setAccessibilityNodeInfoStringAttributes( |
+ env, obj, info, |
+ base::android::ConvertUTF8ToJavaString(env, node->GetClassName()).obj(), |
+ base::android::ConvertUTF16ToJavaString(env, node->GetText()).obj()); |
+ |
+ gfx::Rect absolute_rect = node->GetLocalBoundsRect(); |
+ gfx::Rect parent_relative_rect = absolute_rect; |
+ if (node->parent()) { |
+ gfx::Rect parent_rect = node->parent()->GetLocalBoundsRect(); |
+ parent_relative_rect.Offset(-parent_rect.OffsetFromOrigin()); |
+ } |
+ bool is_root = node->parent() == NULL; |
+ Java_BrowserAccessibilityManager_setAccessibilityNodeInfoLocation( |
+ env, obj, info, |
+ absolute_rect.x(), absolute_rect.y(), |
+ parent_relative_rect.x(), parent_relative_rect.y(), |
+ absolute_rect.width(), absolute_rect.height(), |
+ is_root); |
+ |
+ return true; |
+} |
+ |
+jboolean BrowserAccessibilityManagerAndroid::PopulateAccessibilityEvent( |
+ JNIEnv* env, jobject obj, jobject event, jint id, jint event_type) { |
+ BrowserAccessibilityAndroid* node = static_cast<BrowserAccessibilityAndroid*>( |
+ GetFromRendererID(id)); |
+ if (!node) |
+ return false; |
+ |
+ Java_BrowserAccessibilityManager_setAccessibilityEventBooleanAttributes( |
+ env, obj, event, |
+ node->IsChecked(), |
+ node->IsEnabled(), |
+ node->IsPassword(), |
+ node->IsScrollable()); |
+ Java_BrowserAccessibilityManager_setAccessibilityEventClassName( |
+ env, obj, event, |
+ base::android::ConvertUTF8ToJavaString(env, node->GetClassName()).obj()); |
+ Java_BrowserAccessibilityManager_setAccessibilityEventListAttributes( |
+ env, obj, event, |
+ node->GetItemIndex(), |
+ node->GetItemCount()); |
+ Java_BrowserAccessibilityManager_setAccessibilityEventScrollAttributes( |
+ env, obj, event, |
+ node->GetScrollX(), |
+ node->GetScrollY(), |
+ node->GetMaxScrollX(), |
+ node->GetMaxScrollY()); |
+ |
+ switch (event_type) { |
+ case ANDROID_ACCESSIBILITY_EVENT_TYPE_VIEW_TEXT_CHANGED: |
+ Java_BrowserAccessibilityManager_setAccessibilityEventTextChangedAttrs( |
+ env, obj, event, |
+ node->GetTextChangeFromIndex(), |
+ node->GetTextChangeAddedCount(), |
+ node->GetTextChangeRemovedCount(), |
+ base::android::ConvertUTF16ToJavaString( |
+ env, node->GetTextChangeBeforeText()).obj(), |
+ base::android::ConvertUTF16ToJavaString(env, node->GetText()).obj()); |
+ break; |
+ case ANDROID_ACCESSIBILITY_EVENT_TYPE_VIEW_TEXT_SELECTION_CHANGED: |
+ Java_BrowserAccessibilityManager_setAccessibilityEventSelectionAttrs( |
+ env, obj, event, |
+ node->GetSelectionStart(), |
+ node->GetSelectionEnd(), |
+ node->GetEditableTextLength(), |
+ base::android::ConvertUTF16ToJavaString(env, node->GetText()).obj()); |
+ break; |
+ default: |
+ break; |
+ } |
+ |
+ return true; |
+} |
+ |
+void BrowserAccessibilityManagerAndroid::Click( |
+ JNIEnv* env, jobject obj, jint id) { |
+ BrowserAccessibility* node = GetFromRendererID(id); |
+ if (node) |
+ DoDefaultAction(*node); |
+} |
+ |
+void BrowserAccessibilityManagerAndroid::Focus( |
+ JNIEnv* env, jobject obj, jint id) { |
+ BrowserAccessibility* node = GetFromRendererID(id); |
+ if (node) |
+ SetFocus(node, true); |
+} |
+ |
+void BrowserAccessibilityManagerAndroid::Blur(JNIEnv* env, jobject obj) { |
+ SetFocus(root_, true); |
+} |
+ |
BrowserAccessibility* BrowserAccessibilityManagerAndroid::FuzzyHitTest( |
int x, int y, BrowserAccessibility* start_node) { |
BrowserAccessibility* nearest_node = NULL; |
@@ -135,7 +317,7 @@ void BrowserAccessibilityManagerAndroid::FuzzyHitTestImpl( |
return; |
} |
- if (!node->ComputeName().empty()) { |
+ if (!node->GetText().empty()) { |
if (distance < *nearest_distance) { |
*nearest_candidate = node; |
*nearest_distance = distance; |
@@ -162,13 +344,13 @@ int BrowserAccessibilityManagerAndroid::CalculateDistanceSquared( |
return dx * dx + dy * dy; |
} |
-jint BrowserAccessibilityManagerAndroid::GetNativeNodeById( |
- JNIEnv* env, jobject obj, jint id) { |
- return reinterpret_cast<jint>(GetFromRendererID(id)); |
-} |
- |
void BrowserAccessibilityManagerAndroid::NotifyRootChanged() { |
- // TODO(aboxhall): non-stub implementation |
+ JNIEnv* env = AttachCurrentThread(); |
+ ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); |
+ if (obj.is_null()) |
+ return; |
+ |
+ Java_BrowserAccessibilityManager_handleNavigate(env, obj.obj()); |
} |
bool |
@@ -178,8 +360,7 @@ BrowserAccessibilityManagerAndroid::UseRootScrollOffsetsWhenComputingBounds() { |
} |
bool RegisterBrowserAccessibilityManager(JNIEnv* env) { |
- // TODO(aboxhall): non-stub implementation |
- return false; |
+ return RegisterNativesImpl(env); |
} |
} // namespace content |