| OLD | NEW |
| 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 <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include <cmath> | 9 #include <cmath> |
| 10 | 10 |
| 11 #include "base/android/jni_android.h" | 11 #include "base/android/jni_android.h" |
| 12 #include "base/android/jni_string.h" | 12 #include "base/android/jni_string.h" |
| 13 #include "base/i18n/char_iterator.h" | 13 #include "base/i18n/char_iterator.h" |
| 14 #include "base/strings/string_number_conversions.h" | 14 #include "base/strings/string_number_conversions.h" |
| 15 #include "base/strings/utf_string_conversions.h" | 15 #include "base/strings/utf_string_conversions.h" |
| 16 #include "base/values.h" | 16 #include "base/values.h" |
| 17 #include "content/browser/accessibility/browser_accessibility_android.h" | 17 #include "content/browser/accessibility/browser_accessibility_android.h" |
| 18 #include "content/browser/accessibility/one_shot_accessibility_tree_search.h" | 18 #include "content/browser/accessibility/one_shot_accessibility_tree_search.h" |
| 19 #include "content/common/accessibility_messages.h" | 19 #include "content/common/accessibility_messages.h" |
| 20 #include "jni/BrowserAccessibilityManager_jni.h" | 20 #include "jni/BrowserAccessibilityManager_jni.h" |
| 21 #include "ui/accessibility/ax_text_utils.h" | 21 #include "ui/accessibility/ax_text_utils.h" |
| 22 | 22 |
| 23 using base::android::AttachCurrentThread; | 23 using base::android::AttachCurrentThread; |
| 24 using base::android::ScopedJavaLocalRef; | 24 using base::android::ScopedJavaLocalRef; |
| 25 | 25 |
| 26 namespace content { | 26 namespace content { |
| 27 | 27 |
| 28 namespace { | 28 namespace { |
| 29 | 29 |
| 30 using SearchKeyToPredicateMap = |
| 31 base::hash_map<base::string16, AccessibilityMatchPredicate>; |
| 32 base::LazyInstance<SearchKeyToPredicateMap> g_search_key_to_predicate_map = |
| 33 LAZY_INSTANCE_INITIALIZER; |
| 34 base::LazyInstance<base::string16> g_all_search_keys = |
| 35 LAZY_INSTANCE_INITIALIZER; |
| 36 |
| 37 bool SectionPredicate( |
| 38 BrowserAccessibility* start, BrowserAccessibility* node) { |
| 39 switch (node->GetRole()) { |
| 40 case ui::AX_ROLE_ARTICLE: |
| 41 case ui::AX_ROLE_APPLICATION: |
| 42 case ui::AX_ROLE_BANNER: |
| 43 case ui::AX_ROLE_COMPLEMENTARY: |
| 44 case ui::AX_ROLE_CONTENT_INFO: |
| 45 case ui::AX_ROLE_HEADING: |
| 46 case ui::AX_ROLE_MAIN: |
| 47 case ui::AX_ROLE_NAVIGATION: |
| 48 case ui::AX_ROLE_SEARCH: |
| 49 case ui::AX_ROLE_REGION: |
| 50 return true; |
| 51 default: |
| 52 return false; |
| 53 } |
| 54 } |
| 55 |
| 56 void AddToPredicateMap(const char* search_key_ascii, |
| 57 AccessibilityMatchPredicate predicate) { |
| 58 base::string16 search_key_utf16 = base::ASCIIToUTF16(search_key_ascii); |
| 59 g_search_key_to_predicate_map.Get()[search_key_utf16] = predicate; |
| 60 if (!g_all_search_keys.Get().empty()) |
| 61 g_all_search_keys.Get() += base::ASCIIToUTF16(","); |
| 62 g_all_search_keys.Get() += search_key_utf16; |
| 63 } |
| 64 |
| 30 // These are special unofficial strings sent from TalkBack/BrailleBack | 65 // These are special unofficial strings sent from TalkBack/BrailleBack |
| 31 // to jump to certain categories of web elements. | 66 // to jump to certain categories of web elements. |
| 32 AccessibilityMatchPredicate PredicateForSearchKey(base::string16 element_type) { | 67 void InitSearchKeyToPredicateMapIfNeeded() { |
| 33 if (element_type == base::ASCIIToUTF16("SECTION")) { | 68 if (!g_search_key_to_predicate_map.Get().empty()) |
| 34 return [](BrowserAccessibility* start, BrowserAccessibility* node) { | 69 return; |
| 35 switch (node->GetRole()) { | 70 |
| 36 case ui::AX_ROLE_ARTICLE: | 71 AddToPredicateMap("ARTICLE", AccessibilityArticlePredicate); |
| 37 case ui::AX_ROLE_APPLICATION: | 72 AddToPredicateMap("BUTTON", AccessibilityButtonPredicate); |
| 38 case ui::AX_ROLE_BANNER: | 73 AddToPredicateMap("CHECKBOX", AccessibilityCheckboxPredicate); |
| 39 case ui::AX_ROLE_COMPLEMENTARY: | 74 AddToPredicateMap("COMBOBOX", AccessibilityComboboxPredicate); |
| 40 case ui::AX_ROLE_CONTENT_INFO: | 75 AddToPredicateMap("CONTROL", AccessibilityControlPredicate); |
| 41 case ui::AX_ROLE_HEADING: | 76 AddToPredicateMap("FOCUSABLE", AccessibilityFocusablePredicate); |
| 42 case ui::AX_ROLE_MAIN: | 77 AddToPredicateMap("FRAME", AccessibilityFramePredicate); |
| 43 case ui::AX_ROLE_NAVIGATION: | 78 AddToPredicateMap("GRAPHIC", AccessibilityGraphicPredicate); |
| 44 case ui::AX_ROLE_SEARCH: | 79 AddToPredicateMap("H1", AccessibilityH1Predicate); |
| 45 case ui::AX_ROLE_REGION: | 80 AddToPredicateMap("H2", AccessibilityH2Predicate); |
| 46 return true; | 81 AddToPredicateMap("H3", AccessibilityH3Predicate); |
| 47 default: | 82 AddToPredicateMap("H4", AccessibilityH4Predicate); |
| 48 return false; | 83 AddToPredicateMap("H5", AccessibilityH5Predicate); |
| 49 } | 84 AddToPredicateMap("H6", AccessibilityH6Predicate); |
| 50 }; | 85 AddToPredicateMap("HEADING", AccessibilityHeadingPredicate); |
| 51 } else if (element_type == base::ASCIIToUTF16("LIST")) { | 86 AddToPredicateMap("LANDMARK", AccessibilityLandmarkPredicate); |
| 52 return AccessibilityListPredicate; | 87 AddToPredicateMap("LINK", AccessibilityLinkPredicate); |
| 53 } else if (element_type == base::ASCIIToUTF16("CONTROL")) { | 88 AddToPredicateMap("LIST", AccessibilityListPredicate); |
| 54 return AccessibilityControlPredicate; | 89 AddToPredicateMap("LIST_ITEM", AccessibilityListItemPredicate); |
| 55 } else if (element_type == base::ASCIIToUTF16("ARTICLE")) { | 90 AddToPredicateMap("MAIN", AccessibilityMainPredicate); |
| 56 return AccessibilityArticlePredicate; | 91 AddToPredicateMap("MEDIA", AccessibilityMediaPredicate); |
| 57 } else if (element_type == base::ASCIIToUTF16("BUTTON")) { | 92 AddToPredicateMap("RADIO", AccessibilityRadioButtonPredicate); |
| 58 return AccessibilityButtonPredicate; | 93 AddToPredicateMap("SECTION", SectionPredicate); |
| 59 } else if (element_type == base::ASCIIToUTF16("CHECKBOX")) { | 94 AddToPredicateMap("TABLE", AccessibilityTablePredicate); |
| 60 return AccessibilityCheckboxPredicate; | 95 AddToPredicateMap("TEXT_FIELD", AccessibilityTextfieldPredicate); |
| 61 } else if (element_type == base::ASCIIToUTF16("COMBOBOX")) { | 96 AddToPredicateMap("UNVISITED_LINK", AccessibilityUnvisitedLinkPredicate); |
| 62 return AccessibilityComboboxPredicate; | 97 AddToPredicateMap("VISITED_LINK", AccessibilityVisitedLinkPredicate); |
| 63 } else if (element_type == base::ASCIIToUTF16("TEXT_FIELD")) { | 98 } |
| 64 return AccessibilityTextfieldPredicate; | 99 |
| 65 } else if (element_type == base::ASCIIToUTF16("FOCUSABLE")) { | 100 AccessibilityMatchPredicate PredicateForSearchKey( |
| 66 return AccessibilityFocusablePredicate; | 101 const base::string16& element_type) { |
| 67 } else if (element_type == base::ASCIIToUTF16("GRAPHIC")) { | 102 InitSearchKeyToPredicateMapIfNeeded(); |
| 68 return AccessibilityGraphicPredicate; | 103 const auto& iter = g_search_key_to_predicate_map.Get().find(element_type); |
| 69 } else if (element_type == base::ASCIIToUTF16("HEADING")) { | 104 if (iter != g_search_key_to_predicate_map.Get().end()) |
| 70 return AccessibilityHeadingPredicate; | 105 return iter->second; |
| 71 } else if (element_type == base::ASCIIToUTF16("H1")) { | |
| 72 return AccessibilityH1Predicate; | |
| 73 } else if (element_type == base::ASCIIToUTF16("H2")) { | |
| 74 return AccessibilityH2Predicate; | |
| 75 } else if (element_type == base::ASCIIToUTF16("H3")) { | |
| 76 return AccessibilityH3Predicate; | |
| 77 } else if (element_type == base::ASCIIToUTF16("H4")) { | |
| 78 return AccessibilityH4Predicate; | |
| 79 } else if (element_type == base::ASCIIToUTF16("H5")) { | |
| 80 return AccessibilityH5Predicate; | |
| 81 } else if (element_type == base::ASCIIToUTF16("H6")) { | |
| 82 return AccessibilityH6Predicate; | |
| 83 } else if (element_type == base::ASCIIToUTF16("FRAME")) { | |
| 84 return AccessibilityFramePredicate; | |
| 85 } else if (element_type == base::ASCIIToUTF16("LANDMARK")) { | |
| 86 return AccessibilityLandmarkPredicate; | |
| 87 } else if (element_type == base::ASCIIToUTF16("LINK")) { | |
| 88 return AccessibilityLinkPredicate; | |
| 89 } else if (element_type == base::ASCIIToUTF16("LIST_ITEM")) { | |
| 90 return AccessibilityListItemPredicate; | |
| 91 } else if (element_type == base::ASCIIToUTF16("MAIN")) { | |
| 92 return AccessibilityMainPredicate; | |
| 93 } else if (element_type == base::ASCIIToUTF16("MEDIA")) { | |
| 94 return AccessibilityMediaPredicate; | |
| 95 } else if (element_type == base::ASCIIToUTF16("RADIO")) { | |
| 96 return AccessibilityRadioButtonPredicate; | |
| 97 } else if (element_type == base::ASCIIToUTF16("TABLE")) { | |
| 98 return AccessibilityTablePredicate; | |
| 99 } else if (element_type == base::ASCIIToUTF16("UNVISITED_LINK")) { | |
| 100 return AccessibilityUnvisitedLinkPredicate; | |
| 101 } else if (element_type == base::ASCIIToUTF16("VISITED_LINK")) { | |
| 102 return AccessibilityVisitedLinkPredicate; | |
| 103 } | |
| 104 | 106 |
| 105 // If we don't recognize the selector, return any element that's clickable. | 107 // If we don't recognize the selector, return any element that's clickable. |
| 106 // We mark all focusable nodes and leaf nodes as clickable because it's | 108 // We mark all focusable nodes and leaf nodes as clickable because it's |
| 107 // impossible to know whether a web node has a click handler or not, so | 109 // impossible to know whether a web node has a click handler or not, so |
| 108 // to be safe we have to allow accessibility services to click on nearly | 110 // to be safe we have to allow accessibility services to click on nearly |
| 109 // anything that could possibly respond to a click. | 111 // anything that could possibly respond to a click. |
| 110 return [](BrowserAccessibility* start, BrowserAccessibility* node) { | 112 return [](BrowserAccessibility* start, BrowserAccessibility* node) { |
| 111 return static_cast<BrowserAccessibilityAndroid*>(node)->IsClickable(); | 113 return static_cast<BrowserAccessibilityAndroid*>(node)->IsClickable(); |
| 112 }; | 114 }; |
| 113 } | 115 } |
| (...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 280 if (obj.is_null()) | 282 if (obj.is_null()) |
| 281 return; | 283 return; |
| 282 Java_BrowserAccessibilityManager_sendDelayedWindowContentChangedEvent( | 284 Java_BrowserAccessibilityManager_sendDelayedWindowContentChangedEvent( |
| 283 env, obj.obj()); | 285 env, obj.obj()); |
| 284 return; | 286 return; |
| 285 } | 287 } |
| 286 | 288 |
| 287 BrowserAccessibilityManager::OnLocationChanges(params); | 289 BrowserAccessibilityManager::OnLocationChanges(params); |
| 288 } | 290 } |
| 289 | 291 |
| 292 base::android::ScopedJavaLocalRef<jstring> |
| 293 BrowserAccessibilityManagerAndroid::GetSupportedHtmlElementTypes( |
| 294 JNIEnv* env, |
| 295 const JavaParamRef<jobject>& obj) { |
| 296 InitSearchKeyToPredicateMapIfNeeded(); |
| 297 return base::android::ConvertUTF16ToJavaString(env, g_all_search_keys.Get()); |
| 298 } |
| 299 |
| 290 jint BrowserAccessibilityManagerAndroid::GetRootId( | 300 jint BrowserAccessibilityManagerAndroid::GetRootId( |
| 291 JNIEnv* env, | 301 JNIEnv* env, |
| 292 const JavaParamRef<jobject>& obj) { | 302 const JavaParamRef<jobject>& obj) { |
| 293 if (GetRoot()) | 303 if (GetRoot()) |
| 294 return static_cast<jint>(GetRoot()->GetId()); | 304 return static_cast<jint>(GetRoot()->GetId()); |
| 295 else | 305 else |
| 296 return -1; | 306 return -1; |
| 297 } | 307 } |
| 298 | 308 |
| 299 jboolean BrowserAccessibilityManagerAndroid::IsNodeValid( | 309 jboolean BrowserAccessibilityManagerAndroid::IsNodeValid( |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 420 Java_BrowserAccessibilityManager_setAccessibilityNodeInfoLocation( | 430 Java_BrowserAccessibilityManager_setAccessibilityNodeInfoLocation( |
| 421 env, obj, info, | 431 env, obj, info, |
| 422 id, | 432 id, |
| 423 absolute_rect.x(), absolute_rect.y(), | 433 absolute_rect.x(), absolute_rect.y(), |
| 424 parent_relative_rect.x(), parent_relative_rect.y(), | 434 parent_relative_rect.x(), parent_relative_rect.y(), |
| 425 absolute_rect.width(), absolute_rect.height(), | 435 absolute_rect.width(), absolute_rect.height(), |
| 426 is_root); | 436 is_root); |
| 427 | 437 |
| 428 Java_BrowserAccessibilityManager_setAccessibilityNodeInfoKitKatAttributes( | 438 Java_BrowserAccessibilityManager_setAccessibilityNodeInfoKitKatAttributes( |
| 429 env, obj, info, | 439 env, obj, info, |
| 440 is_root, |
| 430 base::android::ConvertUTF16ToJavaString( | 441 base::android::ConvertUTF16ToJavaString( |
| 431 env, node->GetRoleDescription()).obj()); | 442 env, node->GetRoleDescription()).obj()); |
| 432 | 443 |
| 433 Java_BrowserAccessibilityManager_setAccessibilityNodeInfoLollipopAttributes( | 444 Java_BrowserAccessibilityManager_setAccessibilityNodeInfoLollipopAttributes( |
| 434 env, obj, info, | 445 env, obj, info, |
| 435 node->CanOpenPopup(), | 446 node->CanOpenPopup(), |
| 436 node->IsContentInvalid(), | 447 node->IsContentInvalid(), |
| 437 node->IsDismissable(), | 448 node->IsDismissable(), |
| 438 node->IsMultiLine(), | 449 node->IsMultiLine(), |
| 439 node->AndroidInputType(), | 450 node->AndroidInputType(), |
| (...skipping 479 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 919 BrowserAccessibilityManagerAndroid::UseRootScrollOffsetsWhenComputingBounds() { | 930 BrowserAccessibilityManagerAndroid::UseRootScrollOffsetsWhenComputingBounds() { |
| 920 // The Java layer handles the root scroll offset. | 931 // The Java layer handles the root scroll offset. |
| 921 return false; | 932 return false; |
| 922 } | 933 } |
| 923 | 934 |
| 924 bool RegisterBrowserAccessibilityManager(JNIEnv* env) { | 935 bool RegisterBrowserAccessibilityManager(JNIEnv* env) { |
| 925 return RegisterNativesImpl(env); | 936 return RegisterNativesImpl(env); |
| 926 } | 937 } |
| 927 | 938 |
| 928 } // namespace content | 939 } // namespace content |
| OLD | NEW |