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