| 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
|
|
|