| 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 <cmath> | 7 #include <cmath> |
| 8 | 8 |
| 9 #include "base/android/jni_android.h" | 9 #include "base/android/jni_android.h" |
| 10 #include "base/android/jni_string.h" | 10 #include "base/android/jni_string.h" |
| (...skipping 15 matching lines...) Expand all Loading... |
| 26 ANDROID_ACCESSIBILITY_EVENT_TYPE_VIEW_TEXT_SELECTION_CHANGED = 8192 | 26 ANDROID_ACCESSIBILITY_EVENT_TYPE_VIEW_TEXT_SELECTION_CHANGED = 8192 |
| 27 }; | 27 }; |
| 28 | 28 |
| 29 enum AndroidHtmlElementType { | 29 enum AndroidHtmlElementType { |
| 30 HTML_ELEMENT_TYPE_SECTION, | 30 HTML_ELEMENT_TYPE_SECTION, |
| 31 HTML_ELEMENT_TYPE_LIST, | 31 HTML_ELEMENT_TYPE_LIST, |
| 32 HTML_ELEMENT_TYPE_CONTROL, | 32 HTML_ELEMENT_TYPE_CONTROL, |
| 33 HTML_ELEMENT_TYPE_ANY | 33 HTML_ELEMENT_TYPE_ANY |
| 34 }; | 34 }; |
| 35 | 35 |
| 36 // Restricts |val| to the range [min, max]. | |
| 37 int Clamp(int val, int min, int max) { | |
| 38 return std::min(std::max(val, min), max); | |
| 39 } | |
| 40 | |
| 41 // These are special unofficial strings sent from TalkBack/BrailleBack | 36 // These are special unofficial strings sent from TalkBack/BrailleBack |
| 42 // to jump to certain categories of web elements. | 37 // to jump to certain categories of web elements. |
| 43 AndroidHtmlElementType HtmlElementTypeFromString(base::string16 element_type) { | 38 AndroidHtmlElementType HtmlElementTypeFromString(base::string16 element_type) { |
| 44 if (element_type == base::ASCIIToUTF16("SECTION")) | 39 if (element_type == base::ASCIIToUTF16("SECTION")) |
| 45 return HTML_ELEMENT_TYPE_SECTION; | 40 return HTML_ELEMENT_TYPE_SECTION; |
| 46 else if (element_type == base::ASCIIToUTF16("LIST")) | 41 else if (element_type == base::ASCIIToUTF16("LIST")) |
| 47 return HTML_ELEMENT_TYPE_LIST; | 42 return HTML_ELEMENT_TYPE_LIST; |
| 48 else if (element_type == base::ASCIIToUTF16("CONTROL")) | 43 else if (element_type == base::ASCIIToUTF16("CONTROL")) |
| 49 return HTML_ELEMENT_TYPE_CONTROL; | 44 return HTML_ELEMENT_TYPE_CONTROL; |
| 50 else | 45 else |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 120 ui::AXEvent event_type, | 115 ui::AXEvent event_type, |
| 121 BrowserAccessibility* node) { | 116 BrowserAccessibility* node) { |
| 122 JNIEnv* env = AttachCurrentThread(); | 117 JNIEnv* env = AttachCurrentThread(); |
| 123 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); | 118 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); |
| 124 if (obj.is_null()) | 119 if (obj.is_null()) |
| 125 return; | 120 return; |
| 126 | 121 |
| 127 if (event_type == ui::AX_EVENT_HIDE) | 122 if (event_type == ui::AX_EVENT_HIDE) |
| 128 return; | 123 return; |
| 129 | 124 |
| 125 if (event_type == ui::AX_EVENT_HOVER) { |
| 126 HandleHoverEvent(node); |
| 127 return; |
| 128 } |
| 129 |
| 130 // Always send AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED to notify | 130 // Always send AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED to notify |
| 131 // the Android system that the accessibility hierarchy rooted at this | 131 // the Android system that the accessibility hierarchy rooted at this |
| 132 // node has changed. | 132 // node has changed. |
| 133 Java_BrowserAccessibilityManager_handleContentChanged( | 133 Java_BrowserAccessibilityManager_handleContentChanged( |
| 134 env, obj.obj(), node->GetId()); | 134 env, obj.obj(), node->GetId()); |
| 135 | 135 |
| 136 switch (event_type) { | 136 switch (event_type) { |
| 137 case ui::AX_EVENT_LOAD_COMPLETE: | 137 case ui::AX_EVENT_LOAD_COMPLETE: |
| 138 Java_BrowserAccessibilityManager_handlePageLoaded( | 138 Java_BrowserAccessibilityManager_handlePageLoaded( |
| 139 env, obj.obj(), focus_->id()); | 139 env, obj.obj(), focus_->id()); |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 189 | 189 |
| 190 jint BrowserAccessibilityManagerAndroid::GetRootId(JNIEnv* env, jobject obj) { | 190 jint BrowserAccessibilityManagerAndroid::GetRootId(JNIEnv* env, jobject obj) { |
| 191 return static_cast<jint>(GetRoot()->GetId()); | 191 return static_cast<jint>(GetRoot()->GetId()); |
| 192 } | 192 } |
| 193 | 193 |
| 194 jboolean BrowserAccessibilityManagerAndroid::IsNodeValid( | 194 jboolean BrowserAccessibilityManagerAndroid::IsNodeValid( |
| 195 JNIEnv* env, jobject obj, jint id) { | 195 JNIEnv* env, jobject obj, jint id) { |
| 196 return GetFromID(id) != NULL; | 196 return GetFromID(id) != NULL; |
| 197 } | 197 } |
| 198 | 198 |
| 199 jint BrowserAccessibilityManagerAndroid::HitTest( | 199 void BrowserAccessibilityManagerAndroid::HitTest( |
| 200 JNIEnv* env, jobject obj, jint x, jint y) { | 200 JNIEnv* env, jobject obj, jint x, jint y) { |
| 201 BrowserAccessibilityAndroid* result = | 201 if (delegate()) |
| 202 static_cast<BrowserAccessibilityAndroid*>( | 202 delegate()->AccessibilityHitTest(gfx::Point(x, y)); |
| 203 GetRoot()->BrowserAccessibilityForPoint(gfx::Point(x, y))); | |
| 204 | |
| 205 if (!result) | |
| 206 return GetRoot()->GetId(); | |
| 207 | |
| 208 if (result->IsFocusable()) | |
| 209 return result->GetId(); | |
| 210 | |
| 211 // Examine the children of |result| to find the nearest accessibility focus | |
| 212 // candidate | |
| 213 BrowserAccessibility* nearest_node = FuzzyHitTest(x, y, result); | |
| 214 if (nearest_node) | |
| 215 return nearest_node->GetId(); | |
| 216 | |
| 217 return GetRoot()->GetId(); | |
| 218 } | 203 } |
| 219 | 204 |
| 220 jboolean BrowserAccessibilityManagerAndroid::PopulateAccessibilityNodeInfo( | 205 jboolean BrowserAccessibilityManagerAndroid::PopulateAccessibilityNodeInfo( |
| 221 JNIEnv* env, jobject obj, jobject info, jint id) { | 206 JNIEnv* env, jobject obj, jobject info, jint id) { |
| 222 BrowserAccessibilityAndroid* node = static_cast<BrowserAccessibilityAndroid*>( | 207 BrowserAccessibilityAndroid* node = static_cast<BrowserAccessibilityAndroid*>( |
| 223 GetFromID(id)); | 208 GetFromID(id)); |
| 224 if (!node) | 209 if (!node) |
| 225 return false; | 210 return false; |
| 226 | 211 |
| 227 if (node->GetParent()) { | 212 if (node->GetParent()) { |
| (...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 409 SetFocus(GetRoot(), true); | 394 SetFocus(GetRoot(), true); |
| 410 } | 395 } |
| 411 | 396 |
| 412 void BrowserAccessibilityManagerAndroid::ScrollToMakeNodeVisible( | 397 void BrowserAccessibilityManagerAndroid::ScrollToMakeNodeVisible( |
| 413 JNIEnv* env, jobject obj, jint id) { | 398 JNIEnv* env, jobject obj, jint id) { |
| 414 BrowserAccessibility* node = GetFromID(id); | 399 BrowserAccessibility* node = GetFromID(id); |
| 415 if (node) | 400 if (node) |
| 416 ScrollToMakeVisible(*node, gfx::Rect(node->GetLocation().size())); | 401 ScrollToMakeVisible(*node, gfx::Rect(node->GetLocation().size())); |
| 417 } | 402 } |
| 418 | 403 |
| 419 BrowserAccessibility* BrowserAccessibilityManagerAndroid::FuzzyHitTest( | 404 void BrowserAccessibilityManagerAndroid::HandleHoverEvent( |
| 420 int x, int y, BrowserAccessibility* start_node) { | 405 BrowserAccessibility* node) { |
| 421 BrowserAccessibility* nearest_node = NULL; | 406 JNIEnv* env = AttachCurrentThread(); |
| 422 int min_distance = INT_MAX; | 407 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env); |
| 423 FuzzyHitTestImpl(x, y, start_node, &nearest_node, &min_distance); | 408 if (obj.is_null()) |
| 424 return nearest_node; | 409 return; |
| 425 } | |
| 426 | 410 |
| 427 // static | 411 BrowserAccessibilityAndroid* ancestor = |
| 428 void BrowserAccessibilityManagerAndroid::FuzzyHitTestImpl( | 412 static_cast<BrowserAccessibilityAndroid*>(node->GetParent()); |
| 429 int x, int y, BrowserAccessibility* start_node, | 413 while (ancestor) { |
| 430 BrowserAccessibility** nearest_candidate, int* nearest_distance) { | 414 if (ancestor->PlatformIsLeaf() || |
| 431 BrowserAccessibilityAndroid* node = | 415 (ancestor->IsFocusable() && !ancestor->HasFocusableChild())) { |
| 432 static_cast<BrowserAccessibilityAndroid*>(start_node); | 416 node = ancestor; |
| 433 int distance = CalculateDistanceSquared(x, y, node); | 417 // Don't break - we want the highest ancestor that's focusable or a |
| 434 | 418 // leaf node. |
| 435 if (node->IsFocusable()) { | |
| 436 if (distance < *nearest_distance) { | |
| 437 *nearest_candidate = node; | |
| 438 *nearest_distance = distance; | |
| 439 } | 419 } |
| 440 // Don't examine any more children of focusable node | 420 ancestor = static_cast<BrowserAccessibilityAndroid*>(ancestor->GetParent()); |
| 441 // TODO(aboxhall): what about focusable children? | |
| 442 return; | |
| 443 } | 421 } |
| 444 | 422 |
| 445 if (!node->GetText().empty()) { | 423 Java_BrowserAccessibilityManager_handleHover( |
| 446 if (distance < *nearest_distance) { | 424 env, obj.obj(), node->GetId()); |
| 447 *nearest_candidate = node; | |
| 448 *nearest_distance = distance; | |
| 449 } | |
| 450 return; | |
| 451 } | |
| 452 | |
| 453 for (uint32 i = 0; i < node->PlatformChildCount(); i++) { | |
| 454 BrowserAccessibility* child = node->PlatformGetChild(i); | |
| 455 FuzzyHitTestImpl(x, y, child, nearest_candidate, nearest_distance); | |
| 456 } | |
| 457 } | |
| 458 | |
| 459 // static | |
| 460 int BrowserAccessibilityManagerAndroid::CalculateDistanceSquared( | |
| 461 int x, int y, BrowserAccessibility* node) { | |
| 462 gfx::Rect node_bounds = node->GetLocalBoundsRect(); | |
| 463 int nearest_x = Clamp(x, node_bounds.x(), node_bounds.right()); | |
| 464 int nearest_y = Clamp(y, node_bounds.y(), node_bounds.bottom()); | |
| 465 int dx = std::abs(x - nearest_x); | |
| 466 int dy = std::abs(y - nearest_y); | |
| 467 return dx * dx + dy * dy; | |
| 468 } | 425 } |
| 469 | 426 |
| 470 jint BrowserAccessibilityManagerAndroid::FindElementType( | 427 jint BrowserAccessibilityManagerAndroid::FindElementType( |
| 471 JNIEnv* env, jobject obj, jint start_id, jstring element_type_str, | 428 JNIEnv* env, jobject obj, jint start_id, jstring element_type_str, |
| 472 jboolean forwards) { | 429 jboolean forwards) { |
| 473 BrowserAccessibility* node = GetFromID(start_id); | 430 BrowserAccessibility* node = GetFromID(start_id); |
| 474 if (!node) | 431 if (!node) |
| 475 return 0; | 432 return 0; |
| 476 | 433 |
| 477 AndroidHtmlElementType element_type = HtmlElementTypeFromString( | 434 AndroidHtmlElementType element_type = HtmlElementTypeFromString( |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 537 BrowserAccessibilityManagerAndroid::UseRootScrollOffsetsWhenComputingBounds() { | 494 BrowserAccessibilityManagerAndroid::UseRootScrollOffsetsWhenComputingBounds() { |
| 538 // The Java layer handles the root scroll offset. | 495 // The Java layer handles the root scroll offset. |
| 539 return false; | 496 return false; |
| 540 } | 497 } |
| 541 | 498 |
| 542 bool RegisterBrowserAccessibilityManager(JNIEnv* env) { | 499 bool RegisterBrowserAccessibilityManager(JNIEnv* env) { |
| 543 return RegisterNativesImpl(env); | 500 return RegisterNativesImpl(env); |
| 544 } | 501 } |
| 545 | 502 |
| 546 } // namespace content | 503 } // namespace content |
| OLD | NEW |