Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / content / public / android / java / src / org / chromium / content / browser / accessibility / BrowserAccessibilityManager.java
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
3 // found in the LICENSE file.
4
5 package org.chromium.content.browser.accessibility;
6
7 import android.content.Context;
8 import android.graphics.Rect;
9 import android.os.Build;
10 import android.os.Bundle;
11 import android.text.SpannableString;
12 import android.text.style.URLSpan;
13 import android.view.MotionEvent;
14 import android.view.View;
15 import android.view.ViewGroup;
16 import android.view.ViewParent;
17 import android.view.accessibility.AccessibilityEvent;
18 import android.view.accessibility.AccessibilityManager;
19 import android.view.accessibility.AccessibilityNodeInfo;
20 import android.view.accessibility.AccessibilityNodeProvider;
21
22 import org.chromium.base.CalledByNative;
23 import org.chromium.base.JNINamespace;
24 import org.chromium.content.browser.ContentViewCore;
25 import org.chromium.content.browser.RenderCoordinates;
26
27 import java.util.ArrayList;
28 import java.util.List;
29 import java.util.Locale;
30
31 /**
32  * Native accessibility for a {@link ContentViewCore}.
33  *
34  * This class is safe to load on ICS and can be used to run tests, but
35  * only the subclass, JellyBeanBrowserAccessibilityManager, actually
36  * has a AccessibilityNodeProvider implementation needed for native
37  * accessibility.
38  */
39 @JNINamespace("content")
40 public class BrowserAccessibilityManager {
41     private static final String TAG = "BrowserAccessibilityManager";
42
43     private ContentViewCore mContentViewCore;
44     private final AccessibilityManager mAccessibilityManager;
45     private final RenderCoordinates mRenderCoordinates;
46     private long mNativeObj;
47     private int mAccessibilityFocusId;
48     private Rect mAccessibilityFocusRect;
49     private boolean mIsHovering;
50     private int mLastHoverId = View.NO_ID;
51     private int mCurrentRootId;
52     private final int[] mTempLocation = new int[2];
53     private final ViewGroup mView;
54     private boolean mUserHasTouchExplored;
55     private boolean mPendingScrollToMakeNodeVisible;
56     private boolean mNotifyFrameInfoInitializedCalled;
57
58     /**
59      * Create a BrowserAccessibilityManager object, which is owned by the C++
60      * BrowserAccessibilityManagerAndroid instance, and connects to the content view.
61      * @param nativeBrowserAccessibilityManagerAndroid A pointer to the counterpart native
62      *     C++ object that owns this object.
63      * @param contentViewCore The content view that this object provides accessibility for.
64      */
65     @CalledByNative
66     private static BrowserAccessibilityManager create(long nativeBrowserAccessibilityManagerAndroid,
67             ContentViewCore contentViewCore) {
68         // A bug in the KitKat framework prevents us from using these new APIs.
69         // http://crbug.com/348088/
70         // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
71         //     return new KitKatBrowserAccessibilityManager(
72         //             nativeBrowserAccessibilityManagerAndroid, contentViewCore);
73
74         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
75             return new JellyBeanBrowserAccessibilityManager(
76                     nativeBrowserAccessibilityManagerAndroid, contentViewCore);
77         } else {
78             return new BrowserAccessibilityManager(
79                     nativeBrowserAccessibilityManagerAndroid, contentViewCore);
80         }
81     }
82
83     protected BrowserAccessibilityManager(long nativeBrowserAccessibilityManagerAndroid,
84             ContentViewCore contentViewCore) {
85         mNativeObj = nativeBrowserAccessibilityManagerAndroid;
86         mContentViewCore = contentViewCore;
87         mContentViewCore.setBrowserAccessibilityManager(this);
88         mAccessibilityFocusId = View.NO_ID;
89         mIsHovering = false;
90         mCurrentRootId = View.NO_ID;
91         mView = mContentViewCore.getContainerView();
92         mRenderCoordinates = mContentViewCore.getRenderCoordinates();
93         mAccessibilityManager =
94             (AccessibilityManager) mContentViewCore.getContext()
95             .getSystemService(Context.ACCESSIBILITY_SERVICE);
96     }
97
98     @CalledByNative
99     private void onNativeObjectDestroyed() {
100         if (mContentViewCore.getBrowserAccessibilityManager() == this) {
101             mContentViewCore.setBrowserAccessibilityManager(null);
102         }
103         mNativeObj = 0;
104         mContentViewCore = null;
105     }
106
107     /**
108      * @return An AccessibilityNodeProvider on JellyBean, and null on previous versions.
109      */
110     public AccessibilityNodeProvider getAccessibilityNodeProvider() {
111         return null;
112     }
113
114     /**
115      * @see AccessibilityNodeProvider#createAccessibilityNodeInfo(int)
116      */
117     protected AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
118         if (!mAccessibilityManager.isEnabled() || mNativeObj == 0) {
119             return null;
120         }
121
122         int rootId = nativeGetRootId(mNativeObj);
123
124         if (virtualViewId == View.NO_ID) {
125             return createNodeForHost(rootId);
126         }
127
128         if (!isFrameInfoInitialized()) {
129             return null;
130         }
131
132         final AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(mView);
133         info.setPackageName(mContentViewCore.getContext().getPackageName());
134         info.setSource(mView, virtualViewId);
135
136         if (virtualViewId == rootId) {
137             info.setParent(mView);
138         }
139
140         if (nativePopulateAccessibilityNodeInfo(mNativeObj, info, virtualViewId)) {
141             return info;
142         } else {
143             info.recycle();
144             return null;
145         }
146     }
147
148     /**
149      * @see AccessibilityNodeProvider#findAccessibilityNodeInfosByText(String, int)
150      */
151     protected List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text,
152             int virtualViewId) {
153         return new ArrayList<AccessibilityNodeInfo>();
154     }
155
156     /**
157      * @see AccessibilityNodeProvider#performAction(int, int, Bundle)
158      */
159     protected boolean performAction(int virtualViewId, int action, Bundle arguments) {
160         // We don't support any actions on the host view or nodes
161         // that are not (any longer) in the tree.
162         if (!mAccessibilityManager.isEnabled() || mNativeObj == 0
163                 || !nativeIsNodeValid(mNativeObj, virtualViewId)) {
164             return false;
165         }
166
167         switch (action) {
168             case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS:
169                 if (!moveAccessibilityFocusToId(virtualViewId)) return true;
170
171                 if (!mIsHovering) {
172                     nativeScrollToMakeNodeVisible(
173                             mNativeObj, mAccessibilityFocusId);
174                 } else {
175                     mPendingScrollToMakeNodeVisible = true;
176                 }
177                 return true;
178             case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
179                 if (mAccessibilityFocusId == virtualViewId) {
180                     sendAccessibilityEvent(mAccessibilityFocusId,
181                             AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
182                     mAccessibilityFocusId = View.NO_ID;
183                     mAccessibilityFocusRect = null;
184                 }
185                 return true;
186             case AccessibilityNodeInfo.ACTION_CLICK:
187                 nativeClick(mNativeObj, virtualViewId);
188                 sendAccessibilityEvent(virtualViewId,
189                         AccessibilityEvent.TYPE_VIEW_CLICKED);
190                 return true;
191             case AccessibilityNodeInfo.ACTION_FOCUS:
192                 nativeFocus(mNativeObj, virtualViewId);
193                 return true;
194             case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS:
195                 nativeBlur(mNativeObj);
196                 return true;
197
198             case AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT: {
199                 if (arguments == null)
200                     return false;
201                 String elementType = arguments.getString(
202                     AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING);
203                 if (elementType == null)
204                     return false;
205                 elementType = elementType.toUpperCase(Locale.US);
206                 return jumpToElementType(elementType, true);
207             }
208             case AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT: {
209                 if (arguments == null)
210                     return false;
211                 String elementType = arguments.getString(
212                     AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING);
213                 if (elementType == null)
214                     return false;
215                 elementType = elementType.toUpperCase(Locale.US);
216                 return jumpToElementType(elementType, false);
217             }
218
219             default:
220                 break;
221         }
222         return false;
223     }
224
225     /**
226      * @see View#onHoverEvent(MotionEvent)
227      */
228     public boolean onHoverEvent(MotionEvent event) {
229         if (!mAccessibilityManager.isEnabled() || mNativeObj == 0) {
230             return false;
231         }
232
233         if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
234             mIsHovering = false;
235             if (mPendingScrollToMakeNodeVisible) {
236                 nativeScrollToMakeNodeVisible(
237                         mNativeObj, mAccessibilityFocusId);
238             }
239             mPendingScrollToMakeNodeVisible = false;
240             return true;
241         }
242
243         mIsHovering = true;
244         mUserHasTouchExplored = true;
245         float x = event.getX();
246         float y = event.getY();
247
248         // Convert to CSS coordinates.
249         int cssX = (int) (mRenderCoordinates.fromPixToLocalCss(x));
250         int cssY = (int) (mRenderCoordinates.fromPixToLocalCss(y));
251
252         // This sends an IPC to the render process to do the hit testing.
253         // The response is handled by handleHover.
254         nativeHitTest(mNativeObj, cssX, cssY);
255         return true;
256     }
257
258     /**
259      * Called by ContentViewCore to notify us when the frame info is initialized,
260      * the first time, since until that point, we can't use mRenderCoordinates to transform
261      * web coordinates to screen coordinates.
262      */
263     public void notifyFrameInfoInitialized() {
264         if (mNotifyFrameInfoInitializedCalled)
265             return;
266
267         mNotifyFrameInfoInitializedCalled = true;
268
269         // Invalidate the container view, since the chrome accessibility tree is now
270         // ready and listed as the child of the container view.
271         mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
272
273         // (Re-) focus focused element, since we weren't able to create an
274         // AccessibilityNodeInfo for this element before.
275         if (mAccessibilityFocusId != View.NO_ID) {
276             moveAccessibilityFocusToIdAndRefocusIfNeeded(mAccessibilityFocusId);
277         }
278     }
279
280     private boolean jumpToElementType(String elementType, boolean forwards) {
281         int id = nativeFindElementType(mNativeObj, mAccessibilityFocusId, elementType, forwards);
282         if (id == 0)
283             return false;
284
285         moveAccessibilityFocusToId(id);
286         return true;
287     }
288
289     private boolean moveAccessibilityFocusToId(int newAccessibilityFocusId) {
290         if (newAccessibilityFocusId == mAccessibilityFocusId)
291             return false;
292
293         mAccessibilityFocusId = newAccessibilityFocusId;
294         mAccessibilityFocusRect = null;
295         sendAccessibilityEvent(mAccessibilityFocusId,
296                 AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
297         return true;
298     }
299
300     private void moveAccessibilityFocusToIdAndRefocusIfNeeded(int newAccessibilityFocusId) {
301         // Work around a bug in the Android framework where it doesn't fully update the object
302         // with accessibility focus even if you send it a WINDOW_CONTENT_CHANGED. To work around
303         // this, clear focus and then set focus again.
304         if (newAccessibilityFocusId == mAccessibilityFocusId) {
305             sendAccessibilityEvent(newAccessibilityFocusId,
306                     AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
307             mAccessibilityFocusId = View.NO_ID;
308         }
309
310         moveAccessibilityFocusToId(newAccessibilityFocusId);
311     }
312
313     private void sendAccessibilityEvent(int virtualViewId, int eventType) {
314         // If we don't have any frame info, then the virtual hierarchy
315         // doesn't exist in the view of the Android framework, so should
316         // never send any events.
317         if (!mAccessibilityManager.isEnabled() || mNativeObj == 0
318                 || !isFrameInfoInitialized()) {
319             return;
320         }
321
322         // This is currently needed if we want Android to draw the yellow box around
323         // the item that has accessibility focus. In practice, this doesn't seem to slow
324         // things down, because it's only called when the accessibility focus moves.
325         // TODO(dmazzoni): remove this if/when Android framework fixes bug.
326         mView.postInvalidate();
327
328         // The container view is indicated by a virtualViewId of NO_ID; post these events directly
329         // since there's no web-specific information to attach.
330         if (virtualViewId == View.NO_ID) {
331             mView.sendAccessibilityEvent(eventType);
332             return;
333         }
334
335         final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
336         event.setPackageName(mContentViewCore.getContext().getPackageName());
337         event.setSource(mView, virtualViewId);
338         if (!nativePopulateAccessibilityEvent(mNativeObj, event, virtualViewId, eventType)) {
339             event.recycle();
340             return;
341         }
342
343         mView.requestSendAccessibilityEvent(mView, event);
344     }
345
346     private Bundle getOrCreateBundleForAccessibilityEvent(AccessibilityEvent event) {
347         Bundle bundle = (Bundle) event.getParcelableData();
348         if (bundle == null) {
349             bundle = new Bundle();
350             event.setParcelableData(bundle);
351         }
352         return bundle;
353     }
354
355     private AccessibilityNodeInfo createNodeForHost(int rootId) {
356         // Since we don't want the parent to be focusable, but we can't remove
357         // actions from a node, copy over the necessary fields.
358         final AccessibilityNodeInfo result = AccessibilityNodeInfo.obtain(mView);
359         final AccessibilityNodeInfo source = AccessibilityNodeInfo.obtain(mView);
360         mView.onInitializeAccessibilityNodeInfo(source);
361
362         // Copy over parent and screen bounds.
363         Rect rect = new Rect();
364         source.getBoundsInParent(rect);
365         result.setBoundsInParent(rect);
366         source.getBoundsInScreen(rect);
367         result.setBoundsInScreen(rect);
368
369         // Set up the parent view, if applicable.
370         final ViewParent parent = mView.getParentForAccessibility();
371         if (parent instanceof View) {
372             result.setParent((View) parent);
373         }
374
375         // Populate the minimum required fields.
376         result.setVisibleToUser(source.isVisibleToUser());
377         result.setEnabled(source.isEnabled());
378         result.setPackageName(source.getPackageName());
379         result.setClassName(source.getClassName());
380
381         // Add the Chrome root node.
382         if (isFrameInfoInitialized()) {
383             result.addChild(mView, rootId);
384         }
385
386         return result;
387     }
388
389     /**
390      * Returns whether or not the frame info is initialized, meaning we can safely
391      * convert web coordinates to screen coordinates. When this is first initialized,
392      * notifyFrameInfoInitialized is called - but we shouldn't check whether or not
393      * that method was called as a way to determine if frame info is valid because
394      * notifyFrameInfoInitialized might not be called at all if mRenderCoordinates
395      * gets initialized first.
396      */
397     private boolean isFrameInfoInitialized() {
398         return mRenderCoordinates.getContentWidthCss() != 0.0 ||
399                mRenderCoordinates.getContentHeightCss() != 0.0;
400     }
401
402     @CalledByNative
403     private void handlePageLoaded(int id) {
404         if (mUserHasTouchExplored) return;
405
406         if (mContentViewCore.shouldSetAccessibilityFocusOnPageLoad()) {
407             moveAccessibilityFocusToIdAndRefocusIfNeeded(id);
408         }
409     }
410
411     @CalledByNative
412     private void handleFocusChanged(int id) {
413         sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_FOCUSED);
414         moveAccessibilityFocusToId(id);
415     }
416
417     @CalledByNative
418     private void handleCheckStateChanged(int id) {
419         sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_CLICKED);
420     }
421
422     @CalledByNative
423     private void handleTextSelectionChanged(int id) {
424         sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED);
425     }
426
427     @CalledByNative
428     private void handleEditableTextChanged(int id) {
429         sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
430     }
431
432     @CalledByNative
433     private void handleContentChanged(int id) {
434         int rootId = nativeGetRootId(mNativeObj);
435         if (rootId != mCurrentRootId) {
436             mCurrentRootId = rootId;
437             mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
438         } else {
439             sendAccessibilityEvent(id, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
440         }
441     }
442
443     @CalledByNative
444     private void handleNavigate() {
445         mAccessibilityFocusId = View.NO_ID;
446         mAccessibilityFocusRect = null;
447         mUserHasTouchExplored = false;
448         // Invalidate the host, since its child is now gone.
449         mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
450     }
451
452     @CalledByNative
453     private void handleScrollPositionChanged(int id) {
454         sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_SCROLLED);
455     }
456
457     @CalledByNative
458     private void handleScrolledToAnchor(int id) {
459         moveAccessibilityFocusToId(id);
460     }
461
462     @CalledByNative
463     private void handleHover(int id) {
464         if (mLastHoverId == id) return;
465
466         // Always send the ENTER and then the EXIT event, to match a standard Android View.
467         sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
468         sendAccessibilityEvent(mLastHoverId, AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
469         mLastHoverId = id;
470     }
471
472     @CalledByNative
473     private void announceLiveRegionText(String text) {
474         mView.announceForAccessibility(text);
475     }
476
477     @CalledByNative
478     private void setAccessibilityNodeInfoParent(AccessibilityNodeInfo node, int parentId) {
479         node.setParent(mView, parentId);
480     }
481
482     @CalledByNative
483     private void addAccessibilityNodeInfoChild(AccessibilityNodeInfo node, int childId) {
484         node.addChild(mView, childId);
485     }
486
487     @CalledByNative
488     private void setAccessibilityNodeInfoBooleanAttributes(AccessibilityNodeInfo node,
489             int virtualViewId, boolean checkable, boolean checked, boolean clickable,
490             boolean enabled, boolean focusable, boolean focused, boolean password,
491             boolean scrollable, boolean selected, boolean visibleToUser) {
492         node.setCheckable(checkable);
493         node.setChecked(checked);
494         node.setClickable(clickable);
495         node.setEnabled(enabled);
496         node.setFocusable(focusable);
497         node.setFocused(focused);
498         node.setPassword(password);
499         node.setScrollable(scrollable);
500         node.setSelected(selected);
501         node.setVisibleToUser(visibleToUser);
502
503         node.addAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT);
504         node.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT);
505
506         if (focusable) {
507             if (focused) {
508                 node.addAction(AccessibilityNodeInfo.ACTION_CLEAR_FOCUS);
509             } else {
510                 node.addAction(AccessibilityNodeInfo.ACTION_FOCUS);
511             }
512         }
513
514         if (mAccessibilityFocusId == virtualViewId) {
515             node.setAccessibilityFocused(true);
516             node.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
517         } else {
518             node.setAccessibilityFocused(false);
519             node.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
520         }
521
522         if (clickable) {
523             node.addAction(AccessibilityNodeInfo.ACTION_CLICK);
524         }
525     }
526
527     @CalledByNative
528     private void setAccessibilityNodeInfoClassName(AccessibilityNodeInfo node,
529             String className) {
530         node.setClassName(className);
531     }
532
533     @CalledByNative
534     private void setAccessibilityNodeInfoContentDescription(
535             AccessibilityNodeInfo node, String contentDescription, boolean annotateAsLink) {
536         if (annotateAsLink) {
537             SpannableString spannable = new SpannableString(contentDescription);
538             spannable.setSpan(new URLSpan(""), 0, spannable.length(), 0);
539             node.setContentDescription(spannable);
540         } else {
541             node.setContentDescription(contentDescription);
542         }
543     }
544
545     @CalledByNative
546     private void setAccessibilityNodeInfoLocation(AccessibilityNodeInfo node,
547             final int virtualViewId,
548             int absoluteLeft, int absoluteTop, int parentRelativeLeft, int parentRelativeTop,
549             int width, int height, boolean isRootNode) {
550         // First set the bounds in parent.
551         Rect boundsInParent = new Rect(parentRelativeLeft, parentRelativeTop,
552                 parentRelativeLeft + width, parentRelativeTop + height);
553         if (isRootNode) {
554             // Offset of the web content relative to the View.
555             boundsInParent.offset(0, (int) mRenderCoordinates.getContentOffsetYPix());
556         }
557         node.setBoundsInParent(boundsInParent);
558
559         // Now set the absolute rect, which requires several transformations.
560         Rect rect = new Rect(absoluteLeft, absoluteTop, absoluteLeft + width, absoluteTop + height);
561
562         // Offset by the scroll position.
563         rect.offset(-(int) mRenderCoordinates.getScrollX(),
564                     -(int) mRenderCoordinates.getScrollY());
565
566         // Convert CSS (web) pixels to Android View pixels
567         rect.left = (int) mRenderCoordinates.fromLocalCssToPix(rect.left);
568         rect.top = (int) mRenderCoordinates.fromLocalCssToPix(rect.top);
569         rect.bottom = (int) mRenderCoordinates.fromLocalCssToPix(rect.bottom);
570         rect.right = (int) mRenderCoordinates.fromLocalCssToPix(rect.right);
571
572         // Offset by the location of the web content within the view.
573         rect.offset(0,
574                     (int) mRenderCoordinates.getContentOffsetYPix());
575
576         // Finally offset by the location of the view within the screen.
577         final int[] viewLocation = new int[2];
578         mView.getLocationOnScreen(viewLocation);
579         rect.offset(viewLocation[0], viewLocation[1]);
580
581         node.setBoundsInScreen(rect);
582
583         // Work around a bug in the Android framework where if the object with accessibility
584         // focus moves, the accessibility focus rect is not updated - both the visual highlight,
585         // and the location on the screen that's clicked if you double-tap. To work around this,
586         // when we know the object with accessibility focus moved, move focus away and then
587         // move focus right back to it, which tricks Android into updating its bounds.
588         if (virtualViewId == mAccessibilityFocusId && virtualViewId != mCurrentRootId) {
589             if (mAccessibilityFocusRect == null) {
590                 mAccessibilityFocusRect = rect;
591             } else if (!mAccessibilityFocusRect.equals(rect)) {
592                 mAccessibilityFocusRect = rect;
593                 moveAccessibilityFocusToIdAndRefocusIfNeeded(virtualViewId);
594             }
595         }
596     }
597
598     @CalledByNative
599     protected void setAccessibilityNodeInfoKitKatAttributes(AccessibilityNodeInfo node,
600             boolean canOpenPopup,
601             boolean contentInvalid,
602             boolean dismissable,
603             boolean multiLine,
604             int inputType,
605             int liveRegion) {
606         // Requires KitKat or higher.
607     }
608
609     @CalledByNative
610     protected void setAccessibilityNodeInfoCollectionInfo(AccessibilityNodeInfo node,
611             int rowCount, int columnCount, boolean hierarchical) {
612         // Requires KitKat or higher.
613     }
614
615     @CalledByNative
616     protected void setAccessibilityNodeInfoCollectionItemInfo(AccessibilityNodeInfo node,
617             int rowIndex, int rowSpan, int columnIndex, int columnSpan, boolean heading) {
618         // Requires KitKat or higher.
619     }
620
621     @CalledByNative
622     protected void setAccessibilityNodeInfoRangeInfo(AccessibilityNodeInfo node,
623             int rangeType, float min, float max, float current) {
624         // Requires KitKat or higher.
625     }
626
627     @CalledByNative
628     private void setAccessibilityEventBooleanAttributes(AccessibilityEvent event,
629             boolean checked, boolean enabled, boolean password, boolean scrollable) {
630         event.setChecked(checked);
631         event.setEnabled(enabled);
632         event.setPassword(password);
633         event.setScrollable(scrollable);
634     }
635
636     @CalledByNative
637     private void setAccessibilityEventClassName(AccessibilityEvent event, String className) {
638         event.setClassName(className);
639     }
640
641     @CalledByNative
642     private void setAccessibilityEventListAttributes(AccessibilityEvent event,
643             int currentItemIndex, int itemCount) {
644         event.setCurrentItemIndex(currentItemIndex);
645         event.setItemCount(itemCount);
646     }
647
648     @CalledByNative
649     private void setAccessibilityEventScrollAttributes(AccessibilityEvent event,
650             int scrollX, int scrollY, int maxScrollX, int maxScrollY) {
651         event.setScrollX(scrollX);
652         event.setScrollY(scrollY);
653         event.setMaxScrollX(maxScrollX);
654         event.setMaxScrollY(maxScrollY);
655     }
656
657     @CalledByNative
658     private void setAccessibilityEventTextChangedAttrs(AccessibilityEvent event,
659             int fromIndex, int addedCount, int removedCount, String beforeText, String text) {
660         event.setFromIndex(fromIndex);
661         event.setAddedCount(addedCount);
662         event.setRemovedCount(removedCount);
663         event.setBeforeText(beforeText);
664         event.getText().add(text);
665     }
666
667     @CalledByNative
668     private void setAccessibilityEventSelectionAttrs(AccessibilityEvent event,
669             int fromIndex, int addedCount, int itemCount, String text) {
670         event.setFromIndex(fromIndex);
671         event.setAddedCount(addedCount);
672         event.setItemCount(itemCount);
673         event.getText().add(text);
674     }
675
676     @CalledByNative
677     protected void setAccessibilityEventKitKatAttributes(AccessibilityEvent event,
678             boolean canOpenPopup,
679             boolean contentInvalid,
680             boolean dismissable,
681             boolean multiLine,
682             int inputType,
683             int liveRegion) {
684         // Backwards compatibility for KitKat AccessibilityNodeInfo fields.
685         Bundle bundle = getOrCreateBundleForAccessibilityEvent(event);
686         bundle.putBoolean("AccessibilityNodeInfo.canOpenPopup", canOpenPopup);
687         bundle.putBoolean("AccessibilityNodeInfo.contentInvalid", contentInvalid);
688         bundle.putBoolean("AccessibilityNodeInfo.dismissable", dismissable);
689         bundle.putBoolean("AccessibilityNodeInfo.multiLine", multiLine);
690         bundle.putInt("AccessibilityNodeInfo.inputType", inputType);
691         bundle.putInt("AccessibilityNodeInfo.liveRegion", liveRegion);
692     }
693
694     @CalledByNative
695     protected void setAccessibilityEventCollectionInfo(AccessibilityEvent event,
696             int rowCount, int columnCount, boolean hierarchical) {
697         // Backwards compatibility for KitKat AccessibilityNodeInfo fields.
698         Bundle bundle = getOrCreateBundleForAccessibilityEvent(event);
699         bundle.putInt("AccessibilityNodeInfo.CollectionInfo.rowCount", rowCount);
700         bundle.putInt("AccessibilityNodeInfo.CollectionInfo.columnCount", columnCount);
701         bundle.putBoolean("AccessibilityNodeInfo.CollectionInfo.hierarchical", hierarchical);
702     }
703
704     @CalledByNative
705     protected void setAccessibilityEventCollectionItemInfo(AccessibilityEvent event,
706             int rowIndex, int rowSpan, int columnIndex, int columnSpan, boolean heading) {
707         // Backwards compatibility for KitKat AccessibilityNodeInfo fields.
708         Bundle bundle = getOrCreateBundleForAccessibilityEvent(event);
709         bundle.putInt("AccessibilityNodeInfo.CollectionItemInfo.rowIndex", rowIndex);
710         bundle.putInt("AccessibilityNodeInfo.CollectionItemInfo.rowSpan", rowSpan);
711         bundle.putInt("AccessibilityNodeInfo.CollectionItemInfo.columnIndex", columnIndex);
712         bundle.putInt("AccessibilityNodeInfo.CollectionItemInfo.columnSpan", columnSpan);
713         bundle.putBoolean("AccessibilityNodeInfo.CollectionItemInfo.heading", heading);
714     }
715
716     @CalledByNative
717     protected void setAccessibilityEventRangeInfo(AccessibilityEvent event,
718             int rangeType, float min, float max, float current) {
719         // Backwards compatibility for KitKat AccessibilityNodeInfo fields.
720         Bundle bundle = getOrCreateBundleForAccessibilityEvent(event);
721         bundle.putInt("AccessibilityNodeInfo.RangeInfo.type", rangeType);
722         bundle.putFloat("AccessibilityNodeInfo.RangeInfo.min", min);
723         bundle.putFloat("AccessibilityNodeInfo.RangeInfo.max", max);
724         bundle.putFloat("AccessibilityNodeInfo.RangeInfo.current", current);
725     }
726
727     private native int nativeGetRootId(long nativeBrowserAccessibilityManagerAndroid);
728     private native boolean nativeIsNodeValid(long nativeBrowserAccessibilityManagerAndroid, int id);
729     private native void nativeHitTest(long nativeBrowserAccessibilityManagerAndroid, int x, int y);
730     private native boolean nativePopulateAccessibilityNodeInfo(
731         long nativeBrowserAccessibilityManagerAndroid, AccessibilityNodeInfo info, int id);
732     private native boolean nativePopulateAccessibilityEvent(
733         long nativeBrowserAccessibilityManagerAndroid, AccessibilityEvent event, int id,
734         int eventType);
735     private native void nativeClick(long nativeBrowserAccessibilityManagerAndroid, int id);
736     private native void nativeFocus(long nativeBrowserAccessibilityManagerAndroid, int id);
737     private native void nativeBlur(long nativeBrowserAccessibilityManagerAndroid);
738     private native void nativeScrollToMakeNodeVisible(
739             long nativeBrowserAccessibilityManagerAndroid, int id);
740     private native int nativeFindElementType(long nativeBrowserAccessibilityManagerAndroid,
741             int startId, String elementType, boolean forwards);
742 }