| 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 |