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 | |
43 // Constants from AccessibilityNodeInfo defined in the K SDK. | 40 // Constants from AccessibilityNodeInfo defined in the K SDK. |
44 private static final int ACTION_COLLAPSE = 0x00080000; | 41 private static final int ACTION_COLLAPSE = 0x00080000; |
45 private static final int ACTION_EXPAND = 0x00040000; | 42 private static final int ACTION_EXPAND = 0x00040000; |
46 | 43 |
47 // Constants from AccessibilityNodeInfo defined in the L SDK. | 44 // Constants from AccessibilityNodeInfo defined in the L SDK. |
48 private static final int ACTION_SET_TEXT = 0x200000; | 45 private static final int ACTION_SET_TEXT = 0x200000; |
49 private static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE = | 46 private static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE = |
50 "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE"; | 47 "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE"; |
| 48 private static final int WINDOW_CONTENT_CHANGED_DELAY_MS = 500; |
51 | 49 |
52 // Constants from AccessibilityNodeInfo defined in the M SDK. | 50 // Constants from AccessibilityNodeInfo defined in the M SDK. |
53 // Source: https://developer.android.com/reference/android/R.id.html | 51 // Source: https://developer.android.com/reference/android/R.id.html |
54 protected static final int ACTION_CONTEXT_CLICK = 0x0102003c; | 52 protected static final int ACTION_CONTEXT_CLICK = 0x0102003c; |
55 protected static final int ACTION_SHOW_ON_SCREEN = 0x01020036; | 53 protected static final int ACTION_SHOW_ON_SCREEN = 0x01020036; |
56 protected static final int ACTION_SCROLL_UP = 0x01020038; | 54 protected static final int ACTION_SCROLL_UP = 0x01020038; |
57 protected static final int ACTION_SCROLL_DOWN = 0x0102003a; | 55 protected static final int ACTION_SCROLL_DOWN = 0x0102003a; |
58 protected static final int ACTION_SCROLL_LEFT = 0x01020039; | 56 protected static final int ACTION_SCROLL_LEFT = 0x01020039; |
59 protected static final int ACTION_SCROLL_RIGHT = 0x0102003b; | 57 protected static final int ACTION_SCROLL_RIGHT = 0x0102003b; |
60 | 58 |
61 private final AccessibilityNodeProvider mAccessibilityNodeProvider; | 59 private final AccessibilityNodeProvider mAccessibilityNodeProvider; |
62 private ContentViewCore mContentViewCore; | 60 private ContentViewCore mContentViewCore; |
63 private final AccessibilityManager mAccessibilityManager; | 61 private final AccessibilityManager mAccessibilityManager; |
64 private final RenderCoordinates mRenderCoordinates; | 62 private final RenderCoordinates mRenderCoordinates; |
65 private long mNativeObj; | 63 private long mNativeObj; |
66 private Rect mAccessibilityFocusRect; | 64 private Rect mAccessibilityFocusRect; |
67 private boolean mIsHovering; | 65 private boolean mIsHovering; |
68 private int mLastHoverId = View.NO_ID; | 66 private int mLastHoverId = View.NO_ID; |
69 protected int mCurrentRootId; | 67 protected int mCurrentRootId; |
70 private final int[] mTempLocation = new int[2]; | 68 private final int[] mTempLocation = new int[2]; |
71 private final ViewGroup mView; | 69 private final ViewGroup mView; |
72 private boolean mUserHasTouchExplored; | 70 private boolean mUserHasTouchExplored; |
73 private boolean mPendingScrollToMakeNodeVisible; | 71 private boolean mPendingScrollToMakeNodeVisible; |
74 private boolean mNotifyFrameInfoInitializedCalled; | 72 private boolean mNotifyFrameInfoInitializedCalled; |
75 private int mSelectionGranularity; | 73 private int mSelectionGranularity; |
76 private int mSelectionStartIndex; | 74 private int mSelectionStartIndex; |
77 private int mSelectionEndIndex; | 75 private int mSelectionEndIndex; |
78 protected int mAccessibilityFocusId; | 76 protected int mAccessibilityFocusId; |
79 private Runnable mSendWindowContentChangedRunnable; | 77 private Runnable mSendWindowContentChangedRunnable; |
80 private Runnable mAccessibilityFocusLocationChangedRunnable; | |
81 | 78 |
82 /** | 79 /** |
83 * Create a BrowserAccessibilityManager object, which is owned by the C++ | 80 * Create a BrowserAccessibilityManager object, which is owned by the C++ |
84 * BrowserAccessibilityManagerAndroid instance, and connects to the content
view. | 81 * BrowserAccessibilityManagerAndroid instance, and connects to the content
view. |
85 * @param nativeBrowserAccessibilityManagerAndroid A pointer to the counterp
art native | 82 * @param nativeBrowserAccessibilityManagerAndroid A pointer to the counterp
art native |
86 * C++ object that owns this object. | 83 * C++ object that owns this object. |
87 * @param contentViewCore The content view that this object provides accessi
bility for. | 84 * @param contentViewCore The content view that this object provides accessi
bility for. |
88 */ | 85 */ |
89 @CalledByNative | 86 @CalledByNative |
90 private static BrowserAccessibilityManager create(long nativeBrowserAccessib
ilityManagerAndroid, | 87 private static BrowserAccessibilityManager create(long nativeBrowserAccessib
ilityManagerAndroid, |
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
233 } | 230 } |
234 return true; | 231 return true; |
235 case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: | 232 case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: |
236 // ALWAYS respond with TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED whe
ther we thought | 233 // ALWAYS respond with TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED whe
ther we thought |
237 // it had focus or not, so that the Android framework cache is c
orrect. | 234 // it had focus or not, so that the Android framework cache is c
orrect. |
238 sendAccessibilityEvent(virtualViewId, | 235 sendAccessibilityEvent(virtualViewId, |
239 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
); | 236 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
); |
240 if (mAccessibilityFocusId == virtualViewId) { | 237 if (mAccessibilityFocusId == virtualViewId) { |
241 mAccessibilityFocusId = View.NO_ID; | 238 mAccessibilityFocusId = View.NO_ID; |
242 mAccessibilityFocusRect = null; | 239 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 } | |
249 } | 240 } |
250 return true; | 241 return true; |
251 case AccessibilityNodeInfo.ACTION_CLICK: | 242 case AccessibilityNodeInfo.ACTION_CLICK: |
252 nativeClick(mNativeObj, virtualViewId); | 243 nativeClick(mNativeObj, virtualViewId); |
253 return true; | 244 return true; |
254 case AccessibilityNodeInfo.ACTION_FOCUS: | 245 case AccessibilityNodeInfo.ACTION_FOCUS: |
255 nativeFocus(mNativeObj, virtualViewId); | 246 nativeFocus(mNativeObj, virtualViewId); |
256 return true; | 247 return true; |
257 case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS: | 248 case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS: |
258 nativeBlur(mNativeObj); | 249 nativeBlur(mNativeObj); |
(...skipping 280 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
539 | 530 |
540 private boolean moveAccessibilityFocusToId(int newAccessibilityFocusId) { | 531 private boolean moveAccessibilityFocusToId(int newAccessibilityFocusId) { |
541 if (newAccessibilityFocusId == mAccessibilityFocusId) return false; | 532 if (newAccessibilityFocusId == mAccessibilityFocusId) return false; |
542 | 533 |
543 mAccessibilityFocusId = newAccessibilityFocusId; | 534 mAccessibilityFocusId = newAccessibilityFocusId; |
544 mAccessibilityFocusRect = null; | 535 mAccessibilityFocusRect = null; |
545 mSelectionGranularity = 0; | 536 mSelectionGranularity = 0; |
546 mSelectionStartIndex = 0; | 537 mSelectionStartIndex = 0; |
547 mSelectionEndIndex = 0; | 538 mSelectionEndIndex = 0; |
548 | 539 |
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 // | |
563 // Calling nativeSetAccessibilityFocus will asynchronously load inline t
ext boxes for | 540 // Calling nativeSetAccessibilityFocus will asynchronously load inline t
ext boxes for |
564 // this node and its subtree. If accessibility focus is on anything othe
r than | 541 // this node and its subtree. If accessibility focus is on anything othe
r than |
565 // the root, do it - otherwise set it to -1 so we don't load inline text
boxes | 542 // the root, do it - otherwise set it to -1 so we don't load inline text
boxes |
566 // for the whole subtree of the root. | 543 // for the whole subtree of the root. |
567 if (mAccessibilityFocusId == mCurrentRootId) { | 544 if (mAccessibilityFocusId == mCurrentRootId) { |
568 nativeSetAccessibilityFocus(mNativeObj, -1); | 545 nativeSetAccessibilityFocus(mNativeObj, -1); |
569 } else { | 546 } else { |
570 nativeSetAccessibilityFocus(mNativeObj, mAccessibilityFocusId); | 547 nativeSetAccessibilityFocus(mNativeObj, mAccessibilityFocusId); |
571 } | 548 } |
572 | 549 |
573 sendAccessibilityEvent(mAccessibilityFocusId, | 550 sendAccessibilityEvent(mAccessibilityFocusId, |
574 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); | 551 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); |
575 return true; | 552 return true; |
576 } | 553 } |
577 | 554 |
578 private void moveAccessibilityFocusToIdAndRefocusIfNeeded(int newAccessibili
tyFocusId) { | 555 private void moveAccessibilityFocusToIdAndRefocusIfNeeded(int newAccessibili
tyFocusId) { |
579 // Work around a bug in the Android framework where it doesn't fully upd
ate the object | 556 // Work around a bug in the Android framework where it doesn't fully upd
ate the object |
580 // with accessibility focus even if you send it a WINDOW_CONTENT_CHANGED
. To work around | 557 // with accessibility focus even if you send it a WINDOW_CONTENT_CHANGED
. To work around |
581 // this, clear focus and then set focus again. | 558 // this, clear focus and then set focus again. |
582 if (newAccessibilityFocusId == mAccessibilityFocusId) { | 559 if (newAccessibilityFocusId == mAccessibilityFocusId) { |
583 sendAccessibilityEvent(newAccessibilityFocusId, | 560 sendAccessibilityEvent(newAccessibilityFocusId, |
584 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); | 561 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); |
585 mAccessibilityFocusId = View.NO_ID; | 562 mAccessibilityFocusId = View.NO_ID; |
586 } | 563 } |
587 | 564 |
588 moveAccessibilityFocusToId(newAccessibilityFocusId); | 565 moveAccessibilityFocusToId(newAccessibilityFocusId); |
589 } | 566 } |
590 | 567 |
591 /** | 568 /** |
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 /** | |
633 * Send a WINDOW_CONTENT_CHANGED event after a short delay. This helps throt
tle such | 569 * Send a WINDOW_CONTENT_CHANGED event after a short delay. This helps throt
tle such |
634 * events from firing too quickly during animations, for example. | 570 * events from firing too quickly during animations, for example. |
635 */ | 571 */ |
636 @CalledByNative | 572 @CalledByNative |
637 private void sendDelayedWindowContentChangedEvent() { | 573 private void sendDelayedWindowContentChangedEvent() { |
638 if (mNativeObj == 0) return; | 574 if (mNativeObj == 0) return; |
639 | 575 |
640 if (mSendWindowContentChangedRunnable != null) return; | 576 if (mSendWindowContentChangedRunnable != null) return; |
641 | 577 |
642 mSendWindowContentChangedRunnable = new Runnable() { | 578 mSendWindowContentChangedRunnable = new Runnable() { |
(...skipping 415 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1058 rect.offset(viewLocation[0], viewLocation[1]); | 994 rect.offset(viewLocation[0], viewLocation[1]); |
1059 | 995 |
1060 // Clip the node's bounding rect to the viewport bounds. | 996 // Clip the node's bounding rect to the viewport bounds. |
1061 int viewportRectTop = viewLocation[1] + (int) mRenderCoordinates.getCont
entOffsetYPix(); | 997 int viewportRectTop = viewLocation[1] + (int) mRenderCoordinates.getCont
entOffsetYPix(); |
1062 int viewportRectBottom = viewportRectTop + mContentViewCore.getViewportH
eightPix(); | 998 int viewportRectBottom = viewportRectTop + mContentViewCore.getViewportH
eightPix(); |
1063 if (rect.top < viewportRectTop) rect.top = viewportRectTop; | 999 if (rect.top < viewportRectTop) rect.top = viewportRectTop; |
1064 if (rect.bottom > viewportRectBottom) rect.bottom = viewportRectBottom; | 1000 if (rect.bottom > viewportRectBottom) rect.bottom = viewportRectBottom; |
1065 | 1001 |
1066 node.setBoundsInScreen(rect); | 1002 node.setBoundsInScreen(rect); |
1067 | 1003 |
1068 // If this is the node with accessibility focus, ensure that its locatio
n on-screen | 1004 // Work around a bug in the Android framework where if the object with a
ccessibility |
1069 // is up-to-date. | 1005 // focus moves, the accessibility focus rect is not updated - both the v
isual highlight, |
| 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. |
1070 if (virtualViewId == mAccessibilityFocusId && virtualViewId != mCurrentR
ootId) { | 1009 if (virtualViewId == mAccessibilityFocusId && virtualViewId != mCurrentR
ootId) { |
1071 if (mAccessibilityFocusRect == null) { | 1010 if (mAccessibilityFocusRect == null) { |
1072 mAccessibilityFocusRect = rect; | 1011 mAccessibilityFocusRect = rect; |
1073 } else if (!mAccessibilityFocusRect.equals(rect)) { | 1012 } else if (!mAccessibilityFocusRect.equals(rect)) { |
1074 mAccessibilityFocusRect = rect; | 1013 mAccessibilityFocusRect = rect; |
1075 updateAccessibilityFocusLocationAfterDelay(); | 1014 moveAccessibilityFocusToIdAndRefocusIfNeeded(virtualViewId); |
1076 } | 1015 } |
1077 } | 1016 } |
1078 } | 1017 } |
1079 | 1018 |
1080 @CalledByNative | 1019 @CalledByNative |
1081 protected void setAccessibilityNodeInfoKitKatAttributes(AccessibilityNodeInf
o node, | 1020 protected void setAccessibilityNodeInfoKitKatAttributes(AccessibilityNodeInf
o node, |
1082 boolean isRoot, boolean isEditableText, String roleDescription) { | 1021 boolean isRoot, boolean isEditableText, String roleDescription) { |
1083 // Requires KitKat or higher. | 1022 // Requires KitKat or higher. |
1084 } | 1023 } |
1085 | 1024 |
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1270 long nativeBrowserAccessibilityManagerAndroid, int id); | 1209 long nativeBrowserAccessibilityManagerAndroid, int id); |
1271 private native boolean nativeIsSlider( | 1210 private native boolean nativeIsSlider( |
1272 long nativeBrowserAccessibilityManagerAndroid, int id); | 1211 long nativeBrowserAccessibilityManagerAndroid, int id); |
1273 private native boolean nativeScroll( | 1212 private native boolean nativeScroll( |
1274 long nativeBrowserAccessibilityManagerAndroid, int id, int direction
); | 1213 long nativeBrowserAccessibilityManagerAndroid, int id, int direction
); |
1275 protected native String nativeGetSupportedHtmlElementTypes( | 1214 protected native String nativeGetSupportedHtmlElementTypes( |
1276 long nativeBrowserAccessibilityManagerAndroid); | 1215 long nativeBrowserAccessibilityManagerAndroid); |
1277 private native void nativeShowContextMenu( | 1216 private native void nativeShowContextMenu( |
1278 long nativeBrowserAccessibilityManagerAndroid, int id); | 1217 long nativeBrowserAccessibilityManagerAndroid, int id); |
1279 } | 1218 } |
OLD | NEW |