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 package org.chromium.content.browser.accessibility; | 5 package org.chromium.content.browser.accessibility; |
6 | 6 |
7 import android.annotation.SuppressLint; | 7 import android.annotation.SuppressLint; |
8 import android.content.Context; | 8 import android.content.Context; |
9 import android.graphics.Rect; | 9 import android.graphics.Rect; |
10 import android.os.Build; | 10 import android.os.Build; |
(...skipping 19 matching lines...) Expand all Loading... |
30 import java.util.List; | 30 import java.util.List; |
31 import java.util.Locale; | 31 import java.util.Locale; |
32 | 32 |
33 /** | 33 /** |
34 * Native accessibility for a {@link ContentViewCore}. | 34 * Native accessibility for a {@link ContentViewCore}. |
35 */ | 35 */ |
36 @JNINamespace("content") | 36 @JNINamespace("content") |
37 public class BrowserAccessibilityManager { | 37 public class BrowserAccessibilityManager { |
38 private static final String TAG = "BrowserAccessibilityManager"; | 38 private static final String TAG = "BrowserAccessibilityManager"; |
39 | 39 |
| 40 private static final int WINDOW_CONTENT_CHANGED_DELAY_MS = 500; |
| 41 private static final int ACCESSIBILITY_FOCUS_LOCATION_CHANGED_DELAY_MS = 100
; |
| 42 |
40 // Constants from AccessibilityNodeInfo defined in the K SDK. | 43 // Constants from AccessibilityNodeInfo defined in the K SDK. |
41 private static final int ACTION_COLLAPSE = 0x00080000; | 44 private static final int ACTION_COLLAPSE = 0x00080000; |
42 private static final int ACTION_EXPAND = 0x00040000; | 45 private static final int ACTION_EXPAND = 0x00040000; |
43 | 46 |
44 // Constants from AccessibilityNodeInfo defined in the L SDK. | 47 // Constants from AccessibilityNodeInfo defined in the L SDK. |
45 private static final int ACTION_SET_TEXT = 0x200000; | 48 private static final int ACTION_SET_TEXT = 0x200000; |
46 private static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE = | 49 private static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE = |
47 "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE"; | 50 "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE"; |
48 private static final int WINDOW_CONTENT_CHANGED_DELAY_MS = 500; | |
49 | 51 |
50 // Constants from AccessibilityNodeInfo defined in the M SDK. | 52 // Constants from AccessibilityNodeInfo defined in the M SDK. |
51 // Source: https://developer.android.com/reference/android/R.id.html | 53 // Source: https://developer.android.com/reference/android/R.id.html |
52 protected static final int ACTION_CONTEXT_CLICK = 0x0102003c; | 54 protected static final int ACTION_CONTEXT_CLICK = 0x0102003c; |
53 protected static final int ACTION_SHOW_ON_SCREEN = 0x01020036; | 55 protected static final int ACTION_SHOW_ON_SCREEN = 0x01020036; |
54 protected static final int ACTION_SCROLL_UP = 0x01020038; | 56 protected static final int ACTION_SCROLL_UP = 0x01020038; |
55 protected static final int ACTION_SCROLL_DOWN = 0x0102003a; | 57 protected static final int ACTION_SCROLL_DOWN = 0x0102003a; |
56 protected static final int ACTION_SCROLL_LEFT = 0x01020039; | 58 protected static final int ACTION_SCROLL_LEFT = 0x01020039; |
57 protected static final int ACTION_SCROLL_RIGHT = 0x0102003b; | 59 protected static final int ACTION_SCROLL_RIGHT = 0x0102003b; |
58 | 60 |
59 private final AccessibilityNodeProvider mAccessibilityNodeProvider; | 61 private final AccessibilityNodeProvider mAccessibilityNodeProvider; |
60 private ContentViewCore mContentViewCore; | 62 private ContentViewCore mContentViewCore; |
61 private final AccessibilityManager mAccessibilityManager; | 63 private final AccessibilityManager mAccessibilityManager; |
62 private final RenderCoordinates mRenderCoordinates; | 64 private final RenderCoordinates mRenderCoordinates; |
63 private long mNativeObj; | 65 private long mNativeObj; |
64 private Rect mAccessibilityFocusRect; | 66 private Rect mAccessibilityFocusRect; |
65 private boolean mIsHovering; | 67 private boolean mIsHovering; |
66 private int mLastHoverId = View.NO_ID; | 68 private int mLastHoverId = View.NO_ID; |
67 protected int mCurrentRootId; | 69 protected int mCurrentRootId; |
68 private final int[] mTempLocation = new int[2]; | 70 private final int[] mTempLocation = new int[2]; |
69 private final ViewGroup mView; | 71 private final ViewGroup mView; |
70 private boolean mUserHasTouchExplored; | 72 private boolean mUserHasTouchExplored; |
71 private boolean mPendingScrollToMakeNodeVisible; | 73 private boolean mPendingScrollToMakeNodeVisible; |
72 private boolean mNotifyFrameInfoInitializedCalled; | 74 private boolean mNotifyFrameInfoInitializedCalled; |
73 private int mSelectionGranularity; | 75 private int mSelectionGranularity; |
74 private int mSelectionStartIndex; | 76 private int mSelectionStartIndex; |
75 private int mSelectionEndIndex; | 77 private int mSelectionEndIndex; |
76 protected int mAccessibilityFocusId; | 78 protected int mAccessibilityFocusId; |
77 private Runnable mSendWindowContentChangedRunnable; | 79 private Runnable mSendWindowContentChangedRunnable; |
| 80 private Runnable mAccessibilityFocusLocationChangedRunnable; |
78 | 81 |
79 /** | 82 /** |
80 * Create a BrowserAccessibilityManager object, which is owned by the C++ | 83 * Create a BrowserAccessibilityManager object, which is owned by the C++ |
81 * BrowserAccessibilityManagerAndroid instance, and connects to the content
view. | 84 * BrowserAccessibilityManagerAndroid instance, and connects to the content
view. |
82 * @param nativeBrowserAccessibilityManagerAndroid A pointer to the counterp
art native | 85 * @param nativeBrowserAccessibilityManagerAndroid A pointer to the counterp
art native |
83 * C++ object that owns this object. | 86 * C++ object that owns this object. |
84 * @param contentViewCore The content view that this object provides accessi
bility for. | 87 * @param contentViewCore The content view that this object provides accessi
bility for. |
85 */ | 88 */ |
86 @CalledByNative | 89 @CalledByNative |
87 private static BrowserAccessibilityManager create(long nativeBrowserAccessib
ilityManagerAndroid, | 90 private static BrowserAccessibilityManager create(long nativeBrowserAccessib
ilityManagerAndroid, |
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
230 } | 233 } |
231 return true; | 234 return true; |
232 case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: | 235 case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: |
233 // ALWAYS respond with TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED whe
ther we thought | 236 // ALWAYS respond with TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED whe
ther we thought |
234 // it had focus or not, so that the Android framework cache is c
orrect. | 237 // it had focus or not, so that the Android framework cache is c
orrect. |
235 sendAccessibilityEvent(virtualViewId, | 238 sendAccessibilityEvent(virtualViewId, |
236 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
); | 239 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
); |
237 if (mAccessibilityFocusId == virtualViewId) { | 240 if (mAccessibilityFocusId == virtualViewId) { |
238 mAccessibilityFocusId = View.NO_ID; | 241 mAccessibilityFocusId = View.NO_ID; |
239 mAccessibilityFocusRect = null; | 242 mAccessibilityFocusRect = null; |
| 243 // If we had a pending callback to update the location of th
e previous object |
| 244 // with accessibility focus, remove it. |
| 245 if (mAccessibilityFocusLocationChangedRunnable != null) { |
| 246 mView.removeCallbacks(mAccessibilityFocusLocationChanged
Runnable); |
| 247 mAccessibilityFocusLocationChangedRunnable = null; |
| 248 } |
240 } | 249 } |
241 return true; | 250 return true; |
242 case AccessibilityNodeInfo.ACTION_CLICK: | 251 case AccessibilityNodeInfo.ACTION_CLICK: |
243 nativeClick(mNativeObj, virtualViewId); | 252 nativeClick(mNativeObj, virtualViewId); |
244 return true; | 253 return true; |
245 case AccessibilityNodeInfo.ACTION_FOCUS: | 254 case AccessibilityNodeInfo.ACTION_FOCUS: |
246 nativeFocus(mNativeObj, virtualViewId); | 255 nativeFocus(mNativeObj, virtualViewId); |
247 return true; | 256 return true; |
248 case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS: | 257 case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS: |
249 nativeBlur(mNativeObj); | 258 nativeBlur(mNativeObj); |
(...skipping 280 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
530 | 539 |
531 private boolean moveAccessibilityFocusToId(int newAccessibilityFocusId) { | 540 private boolean moveAccessibilityFocusToId(int newAccessibilityFocusId) { |
532 if (newAccessibilityFocusId == mAccessibilityFocusId) return false; | 541 if (newAccessibilityFocusId == mAccessibilityFocusId) return false; |
533 | 542 |
534 mAccessibilityFocusId = newAccessibilityFocusId; | 543 mAccessibilityFocusId = newAccessibilityFocusId; |
535 mAccessibilityFocusRect = null; | 544 mAccessibilityFocusRect = null; |
536 mSelectionGranularity = 0; | 545 mSelectionGranularity = 0; |
537 mSelectionStartIndex = 0; | 546 mSelectionStartIndex = 0; |
538 mSelectionEndIndex = 0; | 547 mSelectionEndIndex = 0; |
539 | 548 |
| 549 // If we had a pending callback to update the location of the previous o
bject with |
| 550 // accessibility focus, remove it. |
| 551 if (mAccessibilityFocusLocationChangedRunnable != null) { |
| 552 mView.removeCallbacks(mAccessibilityFocusLocationChangedRunnable); |
| 553 mAccessibilityFocusLocationChangedRunnable = null; |
| 554 } |
| 555 |
| 556 // Call nativeSetAccessibilityFocus. For the most part Chrome doesn't ha
ve a |
| 557 // concept of accessibility focus, but we do two things: (1) auto-focus
certain |
| 558 // roles like links when they get accessibility focus and (2) load inlin
e text boxes |
| 559 // for nodes when they get accessibility focus since inline text boxes a
re expensive |
| 560 // to load and on Android they're only needed for nodes that have input
focus or |
| 561 // accessibility focus. |
| 562 // |
540 // Calling nativeSetAccessibilityFocus will asynchronously load inline t
ext boxes for | 563 // Calling nativeSetAccessibilityFocus will asynchronously load inline t
ext boxes for |
541 // this node and its subtree. If accessibility focus is on anything othe
r than | 564 // this node and its subtree. If accessibility focus is on anything othe
r than |
542 // the root, do it - otherwise set it to -1 so we don't load inline text
boxes | 565 // the root, do it - otherwise set it to -1 so we don't load inline text
boxes |
543 // for the whole subtree of the root. | 566 // for the whole subtree of the root. |
544 if (mAccessibilityFocusId == mCurrentRootId) { | 567 if (mAccessibilityFocusId == mCurrentRootId) { |
545 nativeSetAccessibilityFocus(mNativeObj, -1); | 568 nativeSetAccessibilityFocus(mNativeObj, -1); |
546 } else { | 569 } else { |
547 nativeSetAccessibilityFocus(mNativeObj, mAccessibilityFocusId); | 570 nativeSetAccessibilityFocus(mNativeObj, mAccessibilityFocusId); |
548 } | 571 } |
549 | 572 |
550 sendAccessibilityEvent(mAccessibilityFocusId, | 573 sendAccessibilityEvent(mAccessibilityFocusId, |
551 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); | 574 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); |
552 return true; | 575 return true; |
553 } | 576 } |
554 | 577 |
555 private void moveAccessibilityFocusToIdAndRefocusIfNeeded(int newAccessibili
tyFocusId) { | 578 private void moveAccessibilityFocusToIdAndRefocusIfNeeded(int newAccessibili
tyFocusId) { |
556 // Work around a bug in the Android framework where it doesn't fully upd
ate the object | 579 // Work around a bug in the Android framework where it doesn't fully upd
ate the object |
557 // with accessibility focus even if you send it a WINDOW_CONTENT_CHANGED
. To work around | 580 // with accessibility focus even if you send it a WINDOW_CONTENT_CHANGED
. To work around |
558 // this, clear focus and then set focus again. | 581 // this, clear focus and then set focus again. |
559 if (newAccessibilityFocusId == mAccessibilityFocusId) { | 582 if (newAccessibilityFocusId == mAccessibilityFocusId) { |
560 sendAccessibilityEvent(newAccessibilityFocusId, | 583 sendAccessibilityEvent(newAccessibilityFocusId, |
561 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); | 584 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); |
562 mAccessibilityFocusId = View.NO_ID; | 585 mAccessibilityFocusId = View.NO_ID; |
563 } | 586 } |
564 | 587 |
565 moveAccessibilityFocusToId(newAccessibilityFocusId); | 588 moveAccessibilityFocusToId(newAccessibilityFocusId); |
566 } | 589 } |
567 | 590 |
568 /** | 591 /** |
| 592 * Work around a bug in the Android framework where if the object with acces
sibility |
| 593 * focus moves, the accessibility focus rect is not updated - both the visu
al highlight, |
| 594 * and the location on the screen that's clicked if you double-tap. To work
around this, |
| 595 * when we know the object with accessibility focus moved, move focus away a
nd then |
| 596 * move focus right back to it, which tricks Android into updating its bound
s. |
| 597 * |
| 598 * Do this after a short delay because sometimes the change to the object wi
th accessibility |
| 599 * focus happens just before navigating somewhere else. |
| 600 */ |
| 601 private void updateAccessibilityFocusLocationAfterDelay() { |
| 602 if (mNativeObj == 0) return; |
| 603 |
| 604 if (mAccessibilityFocusLocationChangedRunnable != null) return; |
| 605 |
| 606 mAccessibilityFocusLocationChangedRunnable = new Runnable() { |
| 607 @Override |
| 608 public void run() { |
| 609 updateAccessibilityFocusLocation(); |
| 610 } |
| 611 }; |
| 612 |
| 613 mView.postDelayed(mAccessibilityFocusLocationChangedRunnable, |
| 614 ACCESSIBILITY_FOCUS_LOCATION_CHANGED_DELAY_MS); |
| 615 } |
| 616 |
| 617 /** |
| 618 * See updateAccessibilityFocusLocationAfterDelay for details. |
| 619 */ |
| 620 private void updateAccessibilityFocusLocation() { |
| 621 // This can be called from a timeout, so we need to make sure we're stil
l valid. |
| 622 if (mNativeObj == 0 || mContentViewCore == null || mView == null) return
; |
| 623 |
| 624 if (mAccessibilityFocusLocationChangedRunnable != null) { |
| 625 mView.removeCallbacks(mAccessibilityFocusLocationChangedRunnable); |
| 626 mAccessibilityFocusLocationChangedRunnable = null; |
| 627 } |
| 628 |
| 629 moveAccessibilityFocusToIdAndRefocusIfNeeded(mAccessibilityFocusId); |
| 630 } |
| 631 |
| 632 /** |
569 * Send a WINDOW_CONTENT_CHANGED event after a short delay. This helps throt
tle such | 633 * Send a WINDOW_CONTENT_CHANGED event after a short delay. This helps throt
tle such |
570 * events from firing too quickly during animations, for example. | 634 * events from firing too quickly during animations, for example. |
571 */ | 635 */ |
572 @CalledByNative | 636 @CalledByNative |
573 private void sendDelayedWindowContentChangedEvent() { | 637 private void sendDelayedWindowContentChangedEvent() { |
574 if (mNativeObj == 0) return; | 638 if (mNativeObj == 0) return; |
575 | 639 |
576 if (mSendWindowContentChangedRunnable != null) return; | 640 if (mSendWindowContentChangedRunnable != null) return; |
577 | 641 |
578 mSendWindowContentChangedRunnable = new Runnable() { | 642 mSendWindowContentChangedRunnable = new Runnable() { |
(...skipping 415 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
994 rect.offset(viewLocation[0], viewLocation[1]); | 1058 rect.offset(viewLocation[0], viewLocation[1]); |
995 | 1059 |
996 // Clip the node's bounding rect to the viewport bounds. | 1060 // Clip the node's bounding rect to the viewport bounds. |
997 int viewportRectTop = viewLocation[1] + (int) mRenderCoordinates.getCont
entOffsetYPix(); | 1061 int viewportRectTop = viewLocation[1] + (int) mRenderCoordinates.getCont
entOffsetYPix(); |
998 int viewportRectBottom = viewportRectTop + mContentViewCore.getViewportH
eightPix(); | 1062 int viewportRectBottom = viewportRectTop + mContentViewCore.getViewportH
eightPix(); |
999 if (rect.top < viewportRectTop) rect.top = viewportRectTop; | 1063 if (rect.top < viewportRectTop) rect.top = viewportRectTop; |
1000 if (rect.bottom > viewportRectBottom) rect.bottom = viewportRectBottom; | 1064 if (rect.bottom > viewportRectBottom) rect.bottom = viewportRectBottom; |
1001 | 1065 |
1002 node.setBoundsInScreen(rect); | 1066 node.setBoundsInScreen(rect); |
1003 | 1067 |
1004 // Work around a bug in the Android framework where if the object with a
ccessibility | 1068 // If this is the node with accessibility focus, ensure that its locatio
n on-screen |
1005 // focus moves, the accessibility focus rect is not updated - both the v
isual highlight, | 1069 // is up-to-date. |
1006 // and the location on the screen that's clicked if you double-tap. To w
ork around this, | |
1007 // when we know the object with accessibility focus moved, move focus aw
ay and then | |
1008 // move focus right back to it, which tricks Android into updating its b
ounds. | |
1009 if (virtualViewId == mAccessibilityFocusId && virtualViewId != mCurrentR
ootId) { | 1070 if (virtualViewId == mAccessibilityFocusId && virtualViewId != mCurrentR
ootId) { |
1010 if (mAccessibilityFocusRect == null) { | 1071 if (mAccessibilityFocusRect == null) { |
1011 mAccessibilityFocusRect = rect; | 1072 mAccessibilityFocusRect = rect; |
1012 } else if (!mAccessibilityFocusRect.equals(rect)) { | 1073 } else if (!mAccessibilityFocusRect.equals(rect)) { |
1013 mAccessibilityFocusRect = rect; | 1074 mAccessibilityFocusRect = rect; |
1014 moveAccessibilityFocusToIdAndRefocusIfNeeded(virtualViewId); | 1075 updateAccessibilityFocusLocationAfterDelay(); |
1015 } | 1076 } |
1016 } | 1077 } |
1017 } | 1078 } |
1018 | 1079 |
1019 @CalledByNative | 1080 @CalledByNative |
1020 protected void setAccessibilityNodeInfoKitKatAttributes(AccessibilityNodeInf
o node, | 1081 protected void setAccessibilityNodeInfoKitKatAttributes(AccessibilityNodeInf
o node, |
1021 boolean isRoot, boolean isEditableText, String roleDescription) { | 1082 boolean isRoot, boolean isEditableText, String roleDescription) { |
1022 // Requires KitKat or higher. | 1083 // Requires KitKat or higher. |
1023 } | 1084 } |
1024 | 1085 |
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1209 long nativeBrowserAccessibilityManagerAndroid, int id); | 1270 long nativeBrowserAccessibilityManagerAndroid, int id); |
1210 private native boolean nativeIsSlider( | 1271 private native boolean nativeIsSlider( |
1211 long nativeBrowserAccessibilityManagerAndroid, int id); | 1272 long nativeBrowserAccessibilityManagerAndroid, int id); |
1212 private native boolean nativeScroll( | 1273 private native boolean nativeScroll( |
1213 long nativeBrowserAccessibilityManagerAndroid, int id, int direction
); | 1274 long nativeBrowserAccessibilityManagerAndroid, int id, int direction
); |
1214 protected native String nativeGetSupportedHtmlElementTypes( | 1275 protected native String nativeGetSupportedHtmlElementTypes( |
1215 long nativeBrowserAccessibilityManagerAndroid); | 1276 long nativeBrowserAccessibilityManagerAndroid); |
1216 private native void nativeShowContextMenu( | 1277 private native void nativeShowContextMenu( |
1217 long nativeBrowserAccessibilityManagerAndroid, int id); | 1278 long nativeBrowserAccessibilityManagerAndroid, int id); |
1218 } | 1279 } |
OLD | NEW |