Index: trunk/src/content/browser/accessibility/browser_accessibility_android.cc |
=================================================================== |
--- trunk/src/content/browser/accessibility/browser_accessibility_android.cc (revision 207890) |
+++ trunk/src/content/browser/accessibility/browser_accessibility_android.cc (working copy) |
@@ -4,11 +4,17 @@ |
#include "content/browser/accessibility/browser_accessibility_android.h" |
+#include "base/android/jni_android.h" |
+#include "base/android/jni_registrar.h" |
+#include "base/android/jni_string.h" |
+#include "base/android/scoped_java_ref.h" |
#include "base/strings/utf_string_conversions.h" |
#include "content/browser/accessibility/browser_accessibility_manager_android.h" |
#include "content/common/accessibility_messages.h" |
#include "content/common/accessibility_node_data.h" |
+using base::android::ScopedJavaLocalRef; |
+ |
namespace content { |
// static |
@@ -24,95 +30,101 @@ |
return true; |
} |
-bool BrowserAccessibilityAndroid::IsLeaf() const { |
- if (child_count() == 0) |
- return true; |
+// |
+// Actions, called from Java. |
+// |
- // Iframes are always allowed to contain children. |
- if (IsIframe() || |
- role() == AccessibilityNodeData::ROLE_ROOT_WEB_AREA || |
- role() == AccessibilityNodeData::ROLE_WEB_AREA) { |
- return false; |
- } |
+void BrowserAccessibilityAndroid::FocusJNI(JNIEnv* env, jobject obj) { |
+ manager_->SetFocus(this, true); |
+} |
- // If it has a focusable child, we definitely can't leave out children. |
- if (HasFocusableChild()) |
- return false; |
+void BrowserAccessibilityAndroid::ClickJNI(JNIEnv* env, jobject obj) { |
+ manager_->DoDefaultAction(*this); |
+} |
- // Headings with text can drop their children. |
- string16 name = GetText(); |
- if (role() == AccessibilityNodeData::ROLE_HEADING && !name.empty()) |
- return true; |
+// |
+// Const accessors, called from Java. |
+// |
- // Focusable nodes with text can drop their children. |
- if (HasState(AccessibilityNodeData::STATE_FOCUSABLE) && !name.empty()) |
- return true; |
+ScopedJavaLocalRef<jstring> |
+BrowserAccessibilityAndroid::GetNameJNI(JNIEnv* env, jobject obj) const { |
+ return base::android::ConvertUTF16ToJavaString(env, ComputeName()); |
+} |
- // Nodes with only static text as children can drop their children. |
- if (HasOnlyStaticTextChildren()) |
- return true; |
+ScopedJavaLocalRef<jobject> |
+BrowserAccessibilityAndroid::GetAbsoluteRectJNI( |
+ JNIEnv* env, jobject obj) const { |
+ gfx::Rect rect = GetLocalBoundsRect(); |
- return false; |
+ // TODO(aboxhall): replace with non-stub implementation |
+ return ScopedJavaLocalRef<jobject>(env, NULL); |
} |
-bool BrowserAccessibilityAndroid::IsCheckable() const { |
- bool checkable = false; |
- bool is_aria_pressed_defined; |
- bool is_mixed; |
- GetAriaTristate("aria-pressed", &is_aria_pressed_defined, &is_mixed); |
- if (role() == AccessibilityNodeData::ROLE_CHECKBOX || |
- role() == AccessibilityNodeData::ROLE_RADIO_BUTTON || |
- is_aria_pressed_defined) { |
- checkable = true; |
+ScopedJavaLocalRef<jobject> |
+BrowserAccessibilityAndroid::GetRectInParentJNI( |
+ JNIEnv* env, jobject obj) const { |
+ gfx::Rect rect = GetLocalBoundsRect(); |
+ if (parent()) { |
+ gfx::Rect parent_rect = parent()->GetLocalBoundsRect(); |
+ rect.Offset(-parent_rect.OffsetFromOrigin()); |
} |
- if (HasState(AccessibilityNodeData::STATE_CHECKED)) |
- checkable = true; |
- return checkable; |
-} |
-bool BrowserAccessibilityAndroid::IsChecked() const { |
- return HasState(AccessibilityNodeData::STATE_CHECKED); |
+ // TODO(aboxhall): replace with non-stub implementation |
+ return ScopedJavaLocalRef<jobject>(env, NULL); |
} |
-bool BrowserAccessibilityAndroid::IsClickable() const { |
- return (IsLeaf() && !GetText().empty()); |
+jboolean |
+BrowserAccessibilityAndroid::IsFocusableJNI(JNIEnv* env, jobject obj) const { |
+ return static_cast<jboolean>(IsFocusable()); |
} |
-bool BrowserAccessibilityAndroid::IsEnabled() const { |
- return !HasState(AccessibilityNodeData::STATE_UNAVAILABLE); |
+jboolean |
+BrowserAccessibilityAndroid::IsEditableTextJNI(JNIEnv* env, jobject obj) const { |
+ return IsEditableText(); |
} |
-bool BrowserAccessibilityAndroid::IsFocusable() const { |
- bool focusable = HasState(AccessibilityNodeData::STATE_FOCUSABLE); |
- if (IsIframe() || |
- role() == AccessibilityNodeData::ROLE_WEB_AREA) { |
- focusable = false; |
- } |
- return focusable; |
+jint BrowserAccessibilityAndroid::GetParentJNI(JNIEnv* env, jobject obj) const { |
+ return static_cast<jint>(parent()->renderer_id()); |
} |
-bool BrowserAccessibilityAndroid::IsFocused() const { |
- return manager()->GetFocus(manager()->GetRoot()) == this; |
+jint |
+BrowserAccessibilityAndroid::GetChildCountJNI(JNIEnv* env, jobject obj) const { |
+ if (IsLeaf()) |
+ return 0; |
+ else |
+ return static_cast<jint>(child_count()); |
} |
-bool BrowserAccessibilityAndroid::IsPassword() const { |
- return HasState(AccessibilityNodeData::STATE_PROTECTED); |
+jint BrowserAccessibilityAndroid::GetChildIdAtJNI(JNIEnv* env, |
+ jobject obj, |
+ jint child_index) const { |
+ return static_cast<jint>(GetChild(child_index)->renderer_id()); |
} |
-bool BrowserAccessibilityAndroid::IsScrollable() const { |
- int dummy; |
- return GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_X_MAX, &dummy); |
+jboolean |
+BrowserAccessibilityAndroid::IsCheckableJNI(JNIEnv* env, jobject obj) const { |
+ bool checkable = false; |
+ bool is_aria_pressed_defined; |
+ bool is_mixed; |
+ GetAriaTristate("aria-pressed", &is_aria_pressed_defined, &is_mixed); |
+ if (role() == AccessibilityNodeData::ROLE_CHECKBOX || |
+ role() == AccessibilityNodeData::ROLE_RADIO_BUTTON || |
+ is_aria_pressed_defined) { |
+ checkable = true; |
+ } |
+ if (HasState(AccessibilityNodeData::STATE_CHECKED)) |
+ checkable = true; |
+ return static_cast<jboolean>(checkable); |
} |
-bool BrowserAccessibilityAndroid::IsSelected() const { |
- return HasState(AccessibilityNodeData::STATE_SELECTED); |
+jboolean |
+BrowserAccessibilityAndroid::IsCheckedJNI(JNIEnv* env, jobject obj) const { |
+ return static_cast<jboolean>( |
+ HasState(AccessibilityNodeData::STATE_CHECKED)); |
} |
-bool BrowserAccessibilityAndroid::IsVisibleToUser() const { |
- return !HasState(AccessibilityNodeData::STATE_INVISIBLE); |
-} |
- |
-const char* BrowserAccessibilityAndroid::GetClassName() const { |
+base::android::ScopedJavaLocalRef<jstring> |
+BrowserAccessibilityAndroid::GetClassNameJNI(JNIEnv* env, jobject obj) const { |
const char* class_name = NULL; |
switch(role()) { |
@@ -165,52 +177,53 @@ |
break; |
} |
- return class_name; |
+ return base::android::ConvertUTF8ToJavaString(env, class_name); |
} |
-string16 BrowserAccessibilityAndroid::GetText() const { |
- if (IsIframe() || |
- role() == AccessibilityNodeData::ROLE_WEB_AREA) { |
- return string16(); |
- } |
+jboolean |
+BrowserAccessibilityAndroid::IsEnabledJNI(JNIEnv* env, jobject obj) const { |
+ return static_cast<jboolean>( |
+ !HasState(AccessibilityNodeData::STATE_UNAVAILABLE)); |
+} |
- string16 description; |
- GetStringAttribute(AccessibilityNodeData::ATTR_DESCRIPTION, &description); |
+jboolean |
+BrowserAccessibilityAndroid::IsFocusedJNI(JNIEnv* env, jobject obj) const { |
+ return manager()->GetFocus(manager()->GetRoot()) == this; |
+} |
- string16 text; |
- if (!name().empty()) |
- text = name(); |
- else if (!description.empty()) |
- text = description; |
- else if (!value().empty()) |
- text = value(); |
+jboolean |
+BrowserAccessibilityAndroid::IsPasswordJNI(JNIEnv* env, jobject obj) const { |
+ return static_cast<jboolean>( |
+ HasState(AccessibilityNodeData::STATE_PROTECTED)); |
+} |
- if (text.empty() && HasOnlyStaticTextChildren()) { |
- for (uint32 i = 0; i < child_count(); i++) { |
- BrowserAccessibility* child = GetChild(i); |
- text += static_cast<BrowserAccessibilityAndroid*>(child)->GetText(); |
- } |
- } |
+jboolean |
+BrowserAccessibilityAndroid::IsScrollableJNI(JNIEnv* env, jobject obj) const { |
+ int dummy; |
+ bool scrollable = GetIntAttribute( |
+ AccessibilityNodeData::ATTR_SCROLL_X_MAX, &dummy); |
+ return static_cast<jboolean>(scrollable); |
+} |
- switch(role()) { |
- case AccessibilityNodeData::ROLE_IMAGE_MAP_LINK: |
- case AccessibilityNodeData::ROLE_LINK: |
- case AccessibilityNodeData::ROLE_WEBCORE_LINK: |
- if (!text.empty()) |
- text += ASCIIToUTF16(" "); |
- text += ASCIIToUTF16("Link"); |
- break; |
- case AccessibilityNodeData::ROLE_HEADING: |
- // Only append "heading" if this node already has text. |
- if (!text.empty()) |
- text += ASCIIToUTF16(" Heading"); |
- break; |
- } |
+jboolean |
+BrowserAccessibilityAndroid::IsSelectedJNI(JNIEnv* env, jobject obj) const { |
+ return static_cast<jboolean>( |
+ HasState(AccessibilityNodeData::STATE_SELECTED)); |
+} |
- return text; |
+jboolean |
+BrowserAccessibilityAndroid::IsVisibleJNI(JNIEnv* env, jobject obj) const { |
+ return static_cast<jboolean>( |
+ !HasState(AccessibilityNodeData::STATE_INVISIBLE)); |
} |
-int BrowserAccessibilityAndroid::GetItemIndex() const { |
+base::android::ScopedJavaLocalRef<jstring> |
+BrowserAccessibilityAndroid::GetAriaLiveJNI(JNIEnv* env, jobject obj) const { |
+ return base::android::ConvertUTF16ToJavaString(env, GetAriaLive()); |
+} |
+ |
+jint BrowserAccessibilityAndroid::GetItemIndexJNI( |
+ JNIEnv* env, jobject obj) const { |
int index = 0; |
switch(role()) { |
case AccessibilityNodeData::ROLE_LIST_ITEM: |
@@ -227,10 +240,11 @@ |
break; |
} |
} |
- return index; |
+ return static_cast<jint>(index); |
} |
-int BrowserAccessibilityAndroid::GetItemCount() const { |
+jint |
+BrowserAccessibilityAndroid::GetItemCountJNI(JNIEnv* env, jobject obj) const { |
int count = 0; |
switch(role()) { |
case AccessibilityNodeData::ROLE_LIST: |
@@ -247,34 +261,63 @@ |
break; |
} |
} |
- return count; |
+ return static_cast<jint>(count); |
} |
-int BrowserAccessibilityAndroid::GetScrollX() const { |
+jint |
+BrowserAccessibilityAndroid::GetScrollXJNI(JNIEnv* env, jobject obj) const { |
int value = 0; |
GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_X, &value); |
- return value; |
+ return static_cast<jint>(value); |
} |
-int BrowserAccessibilityAndroid::GetScrollY() const { |
+jint |
+BrowserAccessibilityAndroid::GetScrollYJNI(JNIEnv* env, jobject obj) const { |
int value = 0; |
GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_Y, &value); |
- return value; |
+ return static_cast<jint>(value); |
} |
-int BrowserAccessibilityAndroid::GetMaxScrollX() const { |
+jint |
+BrowserAccessibilityAndroid::GetMaxScrollXJNI(JNIEnv* env, jobject obj) const { |
int value = 0; |
GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_X_MAX, &value); |
- return value; |
+ return static_cast<jint>(value); |
} |
-int BrowserAccessibilityAndroid::GetMaxScrollY() const { |
+jint |
+BrowserAccessibilityAndroid::GetMaxScrollYJNI(JNIEnv* env, jobject obj) const { |
int value = 0; |
GetIntAttribute(AccessibilityNodeData::ATTR_SCROLL_Y_MAX, &value); |
- return value; |
+ return static_cast<jint>(value); |
} |
-int BrowserAccessibilityAndroid::GetTextChangeFromIndex() const { |
+jboolean |
+BrowserAccessibilityAndroid::GetClickableJNI(JNIEnv* env, jobject obj) const { |
+ return (IsLeaf() && !ComputeName().empty()); |
+} |
+ |
+jint BrowserAccessibilityAndroid::GetSelectionStartJNI(JNIEnv* env, jobject obj) |
+ const { |
+ int sel_start = 0; |
+ GetIntAttribute(AccessibilityNodeData::ATTR_TEXT_SEL_START, &sel_start); |
+ return sel_start; |
+} |
+ |
+jint BrowserAccessibilityAndroid::GetSelectionEndJNI(JNIEnv* env, jobject obj) |
+ const { |
+ int sel_end = 0; |
+ GetIntAttribute(AccessibilityNodeData::ATTR_TEXT_SEL_END, &sel_end); |
+ return sel_end; |
+} |
+ |
+jint BrowserAccessibilityAndroid::GetEditableTextLengthJNI( |
+ JNIEnv* env, jobject obj) const { |
+ return value().length(); |
+} |
+ |
+int BrowserAccessibilityAndroid::GetTextChangeFromIndexJNI( |
+ JNIEnv* env, jobject obj) const { |
size_t index = 0; |
while (index < old_value_.length() && |
index < new_value_.length() && |
@@ -284,7 +327,8 @@ |
return index; |
} |
-int BrowserAccessibilityAndroid::GetTextChangeAddedCount() const { |
+jint BrowserAccessibilityAndroid::GetTextChangeAddedCountJNI( |
+ JNIEnv* env, jobject obj) const { |
size_t old_len = old_value_.length(); |
size_t new_len = new_value_.length(); |
size_t left = 0; |
@@ -302,7 +346,8 @@ |
return (new_len - left - right); |
} |
-int BrowserAccessibilityAndroid::GetTextChangeRemovedCount() const { |
+jint BrowserAccessibilityAndroid::GetTextChangeRemovedCountJNI( |
+ JNIEnv* env, jobject obj) const { |
size_t old_len = old_value_.length(); |
size_t new_len = new_value_.length(); |
size_t left = 0; |
@@ -320,24 +365,62 @@ |
return (old_len - left - right); |
} |
-string16 BrowserAccessibilityAndroid::GetTextChangeBeforeText() const { |
- return old_value_; |
+base::android::ScopedJavaLocalRef<jstring> |
+BrowserAccessibilityAndroid::GetTextChangeBeforeTextJNI( |
+ JNIEnv* env, jobject obj) const { |
+ return base::android::ConvertUTF16ToJavaString(env, old_value_); |
} |
-int BrowserAccessibilityAndroid::GetSelectionStart() const { |
- int sel_start = 0; |
- GetIntAttribute(AccessibilityNodeData::ATTR_TEXT_SEL_START, &sel_start); |
- return sel_start; |
-} |
+string16 BrowserAccessibilityAndroid::ComputeName() const { |
+ if (IsIframe() || |
+ role() == AccessibilityNodeData::ROLE_WEB_AREA) { |
+ return string16(); |
+ } |
-int BrowserAccessibilityAndroid::GetSelectionEnd() const { |
- int sel_end = 0; |
- GetIntAttribute(AccessibilityNodeData::ATTR_TEXT_SEL_END, &sel_end); |
- return sel_end; |
+ string16 description; |
+ GetStringAttribute(AccessibilityNodeData::ATTR_DESCRIPTION, |
+ &description); |
+ |
+ string16 text; |
+ if (!name().empty()) |
+ text = name(); |
+ else if (!description.empty()) |
+ text = description; |
+ else if (!value().empty()) |
+ text = value(); |
+ |
+ if (text.empty() && HasOnlyStaticTextChildren()) { |
+ for (uint32 i = 0; i < child_count(); i++) { |
+ BrowserAccessibility* child = GetChild(i); |
+ text += static_cast<BrowserAccessibilityAndroid*>(child)->ComputeName(); |
+ } |
+ } |
+ |
+ switch(role()) { |
+ case AccessibilityNodeData::ROLE_IMAGE_MAP_LINK: |
+ case AccessibilityNodeData::ROLE_LINK: |
+ case AccessibilityNodeData::ROLE_WEBCORE_LINK: |
+ if (!text.empty()) |
+ text += ASCIIToUTF16(" "); |
+ text += ASCIIToUTF16("Link"); |
+ break; |
+ case AccessibilityNodeData::ROLE_HEADING: |
+ // Only append "heading" if this node already has text. |
+ if (!text.empty()) |
+ text += ASCIIToUTF16(" Heading"); |
+ break; |
+ } |
+ |
+ return text; |
} |
-int BrowserAccessibilityAndroid::GetEditableTextLength() const { |
- return value().length(); |
+string16 BrowserAccessibilityAndroid::GetAriaLive() const { |
+ string16 aria_live; |
+ if (GetStringAttribute(AccessibilityNodeData::ATTR_CONTAINER_LIVE_STATUS, |
+ &aria_live)) { |
+ return aria_live; |
+ } |
+ return string16(); |
} |
bool BrowserAccessibilityAndroid::HasFocusableChild() const { |
@@ -366,6 +449,46 @@ |
return html_tag == ASCIIToUTF16("iframe"); |
} |
+bool BrowserAccessibilityAndroid::IsFocusable() const { |
+ bool focusable = HasState(AccessibilityNodeData::STATE_FOCUSABLE); |
+ if (IsIframe() || |
+ role() == AccessibilityNodeData::ROLE_WEB_AREA) { |
+ focusable = false; |
+ } |
+ return focusable; |
+} |
+ |
+bool BrowserAccessibilityAndroid::IsLeaf() const { |
+ if (child_count() == 0) |
+ return true; |
+ |
+ // Iframes are always allowed to contain children. |
+ if (IsIframe() || |
+ role() == AccessibilityNodeData::ROLE_ROOT_WEB_AREA || |
+ role() == AccessibilityNodeData::ROLE_WEB_AREA) { |
+ return false; |
+ } |
+ |
+ // If it has a focusable child, we definitely can't leave out children. |
+ if (HasFocusableChild()) |
+ return false; |
+ |
+ // Headings with text can drop their children. |
+ string16 name = ComputeName(); |
+ if (role() == AccessibilityNodeData::ROLE_HEADING && !name.empty()) |
+ return true; |
+ |
+ // Focusable nodes with text can drop their children. |
+ if (HasState(AccessibilityNodeData::STATE_FOCUSABLE) && !name.empty()) |
+ return true; |
+ |
+ // Nodes with only static text as children can drop their children. |
+ if (HasOnlyStaticTextChildren()) |
+ return true; |
+ |
+ return false; |
+} |
+ |
void BrowserAccessibilityAndroid::PostInitialize() { |
BrowserAccessibility::PostInitialize(); |
@@ -393,7 +516,7 @@ |
!EqualsASCII(aria_live, aria_strings::kAriaLiveAssertive)) |
return; |
- string16 text = GetText(); |
+ string16 text = ComputeName(); |
if (cached_text_ != text) { |
if (!text.empty()) { |
manager_->NotifyAccessibilityEvent(AccessibilityNotificationObjectShow, |
@@ -403,4 +526,9 @@ |
} |
} |
+bool RegisterBrowserAccessibility(JNIEnv* env) { |
+ // TODO(aboxhall): replace with non-stub implementation |
+ return false; |
+} |
+ |
} // namespace content |