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 |