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