1 // Copyright 2017 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 #include "content/browser/accessibility/web_contents_accessibility_android.h"
6
7 #include <memory>
8 #include <string>
9 #include <unordered_map>
10 #include <vector>
11
12 #include "base/android/jni_android.h"
13 #include "base/android/jni_array.h"
14 #include "base/android/jni_string.h"
15 #include "base/debug/crash_logging.h"
16 #include "base/feature_list.h"
17 #include "base/macros.h"
18 #include "base/metrics/histogram_macros.h"
19 #include "base/numerics/ranges.h"
20 #include "content/browser/accessibility/browser_accessibility_android.h"
21 #include "content/browser/accessibility/browser_accessibility_manager_android.h"
22 #include "content/browser/accessibility/browser_accessibility_state_impl.h"
23 #include "content/browser/accessibility/one_shot_accessibility_tree_search.h"
24 #include "content/browser/android/render_widget_host_connector.h"
25 #include "content/browser/renderer_host/render_widget_host_view_android.h"
26 #include "content/browser/web_contents/web_contents_impl.h"
27 #include "content/public/android/content_jni_headers/WebContentsAccessibilityImpl_jni.h"
28 #include "content/public/common/content_features.h"
29 #include "content/public/common/use_zoom_for_dsf_policy.h"
30 #include "ui/events/android/motion_event_android.h"
31
32 using base::android::AttachCurrentThread;
33 using base::android::JavaParamRef;
34 using base::android::ScopedJavaLocalRef;
35
36 namespace content {
37
38 namespace {
39
40 // IMPORTANT!
41 // These values are written to logs. Do not renumber or delete
42 // existing items; add new entries to the end of the list.
43 //
44 // Note: The string names for these enums must correspond with the names of
45 // constants from AccessibilityEvent and AccessibilityServiceInfo, defined
46 // below. For example, UMA_EVENT_ANNOUNCEMENT corresponds to
47 // ACCESSIBILITYEVENT_TYPE_ANNOUNCEMENT via the macro
48 // EVENT_TYPE_HISTOGRAM(event_type_mask, ANNOUNCEMENT).
49 enum {
50 UMA_CAPABILITY_CAN_CONTROL_MAGNIFICATION = 0,
51 UMA_CAPABILITY_CAN_PERFORM_GESTURES = 1,
52 UMA_CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 2,
53 UMA_CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS = 3,
54 UMA_CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 4,
55 UMA_CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT = 5,
56 UMA_EVENT_ANNOUNCEMENT = 6,
57 UMA_EVENT_ASSIST_READING_CONTEXT = 7,
58 UMA_EVENT_GESTURE_DETECTION_END = 8,
59 UMA_EVENT_GESTURE_DETECTION_START = 9,
60 UMA_EVENT_NOTIFICATION_STATE_CHANGED = 10,
61 UMA_EVENT_TOUCH_EXPLORATION_GESTURE_END = 11,
62 UMA_EVENT_TOUCH_EXPLORATION_GESTURE_START = 12,
63 UMA_EVENT_TOUCH_INTERACTION_END = 13,
64 UMA_EVENT_TOUCH_INTERACTION_START = 14,
65 UMA_EVENT_VIEW_ACCESSIBILITY_FOCUSED = 15,
66 UMA_EVENT_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 16,
67 UMA_EVENT_VIEW_CLICKED = 17,
68 UMA_EVENT_VIEW_CONTEXT_CLICKED = 18,
69 UMA_EVENT_VIEW_FOCUSED = 19,
70 UMA_EVENT_VIEW_HOVER_ENTER = 20,
71 UMA_EVENT_VIEW_HOVER_EXIT = 21,
72 UMA_EVENT_VIEW_LONG_CLICKED = 22,
73 UMA_EVENT_VIEW_SCROLLED = 23,
74 UMA_EVENT_VIEW_SELECTED = 24,
75 UMA_EVENT_VIEW_TEXT_CHANGED = 25,
76 UMA_EVENT_VIEW_TEXT_SELECTION_CHANGED = 26,
77 UMA_EVENT_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY = 27,
78 UMA_EVENT_WINDOWS_CHANGED = 28,
79 UMA_EVENT_WINDOW_CONTENT_CHANGED = 29,
80 UMA_EVENT_WINDOW_STATE_CHANGED = 30,
81 UMA_FEEDBACK_AUDIBLE = 31,
82 UMA_FEEDBACK_BRAILLE = 32,
83 UMA_FEEDBACK_GENERIC = 33,
84 UMA_FEEDBACK_HAPTIC = 34,
85 UMA_FEEDBACK_SPOKEN = 35,
86 UMA_FEEDBACK_VISUAL = 36,
87 UMA_FLAG_FORCE_DIRECT_BOOT_AWARE = 37,
88 UMA_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 38,
89 UMA_FLAG_REPORT_VIEW_IDS = 39,
90 UMA_FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 40,
91 UMA_FLAG_REQUEST_FILTER_KEY_EVENTS = 41,
92 UMA_FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 42,
93 UMA_FLAG_RETRIEVE_INTERACTIVE_WINDOWS = 43,
94
95 // This must always be the last enum. It's okay for its value to
96 // increase, but none of the other enum values may change.
97 UMA_ACCESSIBILITYSERVICEINFO_MAX
98 };
99
100 // These are constants from
101 // android.view.accessibility.AccessibilityEvent in Java.
102 //
103 // If you add a new constant, add a new UMA enum above and add a line
104 // to CollectStats(), below.
105 enum {
106 ACCESSIBILITYEVENT_TYPE_VIEW_CLICKED = 0x00000001,
107 ACCESSIBILITYEVENT_TYPE_VIEW_LONG_CLICKED = 0x00000002,
108 ACCESSIBILITYEVENT_TYPE_VIEW_SELECTED = 0x00000004,
109 ACCESSIBILITYEVENT_TYPE_VIEW_FOCUSED = 0x00000008,
110 ACCESSIBILITYEVENT_TYPE_VIEW_TEXT_CHANGED = 0x00000010,
111 ACCESSIBILITYEVENT_TYPE_WINDOW_STATE_CHANGED = 0x00000020,
112 ACCESSIBILITYEVENT_TYPE_NOTIFICATION_STATE_CHANGED = 0x00000040,
113 ACCESSIBILITYEVENT_TYPE_VIEW_HOVER_ENTER = 0x00000080,
114 ACCESSIBILITYEVENT_TYPE_VIEW_HOVER_EXIT = 0x00000100,
115 ACCESSIBILITYEVENT_TYPE_TOUCH_EXPLORATION_GESTURE_START = 0x00000200,
116 ACCESSIBILITYEVENT_TYPE_TOUCH_EXPLORATION_GESTURE_END = 0x00000400,
117 ACCESSIBILITYEVENT_TYPE_WINDOW_CONTENT_CHANGED = 0x00000800,
118 ACCESSIBILITYEVENT_TYPE_VIEW_SCROLLED = 0x00001000,
119 ACCESSIBILITYEVENT_TYPE_VIEW_TEXT_SELECTION_CHANGED = 0x00002000,
120 ACCESSIBILITYEVENT_TYPE_ANNOUNCEMENT = 0x00004000,
121 ACCESSIBILITYEVENT_TYPE_VIEW_ACCESSIBILITY_FOCUSED = 0x00008000,
122 ACCESSIBILITYEVENT_TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 0x00010000,
123 ACCESSIBILITYEVENT_TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY =
124 0x00020000,
125 ACCESSIBILITYEVENT_TYPE_GESTURE_DETECTION_START = 0x00040000,
126 ACCESSIBILITYEVENT_TYPE_GESTURE_DETECTION_END = 0x00080000,
127 ACCESSIBILITYEVENT_TYPE_TOUCH_INTERACTION_START = 0x00100000,
128 ACCESSIBILITYEVENT_TYPE_TOUCH_INTERACTION_END = 0x00200000,
129 ACCESSIBILITYEVENT_TYPE_WINDOWS_CHANGED = 0x00400000,
130 ACCESSIBILITYEVENT_TYPE_VIEW_CONTEXT_CLICKED = 0x00800000,
131 ACCESSIBILITYEVENT_TYPE_ASSIST_READING_CONTEXT = 0x01000000,
132 };
133
134 // These are constants from
135 // android.accessibilityservice.AccessibilityServiceInfo in Java:
136 //
137 // If you add a new constant, add a new UMA enum above and add a line
138 // to CollectStats(), below.
139 enum {
140 ACCESSIBILITYSERVICEINFO_CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT = 0x00000001,
141 ACCESSIBILITYSERVICEINFO_CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION =
142 0x00000002,
143 ACCESSIBILITYSERVICEINFO_CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY =
144 0x00000004,
145 ACCESSIBILITYSERVICEINFO_CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS =
146 0x00000008,
147 ACCESSIBILITYSERVICEINFO_CAPABILITY_CAN_CONTROL_MAGNIFICATION = 0x00000010,
148 ACCESSIBILITYSERVICEINFO_CAPABILITY_CAN_PERFORM_GESTURES = 0x00000020,
149 ACCESSIBILITYSERVICEINFO_FEEDBACK_SPOKEN = 0x0000001,
150 ACCESSIBILITYSERVICEINFO_FEEDBACK_HAPTIC = 0x0000002,
151 ACCESSIBILITYSERVICEINFO_FEEDBACK_AUDIBLE = 0x0000004,
152 ACCESSIBILITYSERVICEINFO_FEEDBACK_VISUAL = 0x0000008,
153 ACCESSIBILITYSERVICEINFO_FEEDBACK_GENERIC = 0x0000010,
154 ACCESSIBILITYSERVICEINFO_FEEDBACK_BRAILLE = 0x0000020,
155 ACCESSIBILITYSERVICEINFO_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x0000002,
156 ACCESSIBILITYSERVICEINFO_FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 0x0000004,
157 ACCESSIBILITYSERVICEINFO_FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 0x00000008,
158 ACCESSIBILITYSERVICEINFO_FLAG_REPORT_VIEW_IDS = 0x00000010,
159 ACCESSIBILITYSERVICEINFO_FLAG_REQUEST_FILTER_KEY_EVENTS = 0x00000020,
160 ACCESSIBILITYSERVICEINFO_FLAG_RETRIEVE_INTERACTIVE_WINDOWS = 0x00000040,
161 ACCESSIBILITYSERVICEINFO_FLAG_FORCE_DIRECT_BOOT_AWARE = 0x00010000,
162 };
163
164 // These macros simplify recording a histogram based on information we get
165 // from an AccessibilityService. There are four bitmasks of information
166 // we get from each AccessibilityService, and for each possible bit mask
167 // corresponding to one flag, we want to check if that flag is set, and if
168 // so, record the corresponding histogram.
169 //
170 // Doing this with macros reduces the chance for human error by
171 // recording the wrong histogram for the wrong flag.
172 //
173 // These macros are used by CollectStats(), below.
174 #define EVENT_TYPE_HISTOGRAM(event_type_mask, event_type) \
175 if (event_type_mask & ACCESSIBILITYEVENT_TYPE_##event_type) \
176 UMA_HISTOGRAM_ENUMERATION("Accessibility.AndroidServiceInfo", \
177 UMA_EVENT_##event_type, \
178 UMA_ACCESSIBILITYSERVICEINFO_MAX)
179 #define FLAGS_HISTOGRAM(flags_mask, flag) \
180 if (flags_mask & ACCESSIBILITYSERVICEINFO_FLAG_##flag) \
181 UMA_HISTOGRAM_ENUMERATION("Accessibility.AndroidServiceInfo", \
182 UMA_FLAG_##flag, UMA_ACCESSIBILITYSERVICEINFO_MAX)
183 #define FEEDBACK_TYPE_HISTOGRAM(feedback_type_mask, feedback_type) \
184 if (feedback_type_mask & ACCESSIBILITYSERVICEINFO_FEEDBACK_##feedback_type) \
185 UMA_HISTOGRAM_ENUMERATION("Accessibility.AndroidServiceInfo", \
186 UMA_FEEDBACK_##feedback_type, \
187 UMA_ACCESSIBILITYSERVICEINFO_MAX)
188 #define CAPABILITY_TYPE_HISTOGRAM(capability_type_mask, capability_type) \
189 if (capability_type_mask & \
190 ACCESSIBILITYSERVICEINFO_CAPABILITY_##capability_type) \
191 UMA_HISTOGRAM_ENUMERATION("Accessibility.AndroidServiceInfo", \
192 UMA_CAPABILITY_##capability_type, \
193 UMA_ACCESSIBILITYSERVICEINFO_MAX)
194
195 using SearchKeyToPredicateMap =
196 std::unordered_map<base::string16, AccessibilityMatchPredicate>;
197 base::LazyInstance<SearchKeyToPredicateMap>::Leaky
198 g_search_key_to_predicate_map = LAZY_INSTANCE_INITIALIZER;
199 base::LazyInstance<base::string16>::Leaky g_all_search_keys =
200 LAZY_INSTANCE_INITIALIZER;
201
SectionPredicate(BrowserAccessibility * start,BrowserAccessibility * node)202 bool SectionPredicate(BrowserAccessibility* start, BrowserAccessibility* node) {
203 switch (node->GetRole()) {
204 case ax::mojom::Role::kApplication:
205 case ax::mojom::Role::kArticle:
206 case ax::mojom::Role::kBanner:
207 case ax::mojom::Role::kComplementary:
208 case ax::mojom::Role::kContentInfo:
209 case ax::mojom::Role::kHeading:
210 case ax::mojom::Role::kMain:
211 case ax::mojom::Role::kNavigation:
212 case ax::mojom::Role::kRegion:
213 case ax::mojom::Role::kSearch:
214 case ax::mojom::Role::kSection:
215 return true;
216 default:
217 return false;
218 }
219 }
220
AllInterestingNodesPredicate(BrowserAccessibility * start,BrowserAccessibility * node)221 bool AllInterestingNodesPredicate(BrowserAccessibility* start,
222 BrowserAccessibility* node) {
223 BrowserAccessibilityAndroid* android_node =
224 static_cast<BrowserAccessibilityAndroid*>(node);
225 return android_node->IsInterestingOnAndroid();
226 }
227
AddToPredicateMap(const char * search_key_ascii,AccessibilityMatchPredicate predicate)228 void AddToPredicateMap(const char* search_key_ascii,
229 AccessibilityMatchPredicate predicate) {
230 base::string16 search_key_utf16 = base::ASCIIToUTF16(search_key_ascii);
231 g_search_key_to_predicate_map.Get()[search_key_utf16] = predicate;
232 if (!g_all_search_keys.Get().empty())
233 g_all_search_keys.Get() += base::ASCIIToUTF16(",");
234 g_all_search_keys.Get() += search_key_utf16;
235 }
236
237 // These are special unofficial strings sent from TalkBack/BrailleBack
238 // to jump to certain categories of web elements.
InitSearchKeyToPredicateMapIfNeeded()239 void InitSearchKeyToPredicateMapIfNeeded() {
240 if (!g_search_key_to_predicate_map.Get().empty())
241 return;
242
243 AddToPredicateMap("ARTICLE", AccessibilityArticlePredicate);
244 AddToPredicateMap("BUTTON", AccessibilityButtonPredicate);
245 AddToPredicateMap("CHECKBOX", AccessibilityCheckboxPredicate);
246 AddToPredicateMap("COMBOBOX", AccessibilityComboboxPredicate);
247 AddToPredicateMap("CONTROL", AccessibilityControlPredicate);
248 AddToPredicateMap("FOCUSABLE", AccessibilityFocusablePredicate);
249 AddToPredicateMap("FRAME", AccessibilityFramePredicate);
250 AddToPredicateMap("GRAPHIC", AccessibilityGraphicPredicate);
251 AddToPredicateMap("H1", AccessibilityH1Predicate);
252 AddToPredicateMap("H2", AccessibilityH2Predicate);
253 AddToPredicateMap("H3", AccessibilityH3Predicate);
254 AddToPredicateMap("H4", AccessibilityH4Predicate);
255 AddToPredicateMap("H5", AccessibilityH5Predicate);
256 AddToPredicateMap("H6", AccessibilityH6Predicate);
257 AddToPredicateMap("HEADING", AccessibilityHeadingPredicate);
258 AddToPredicateMap("LANDMARK", AccessibilityLandmarkPredicate);
259 AddToPredicateMap("LINK", AccessibilityLinkPredicate);
260 AddToPredicateMap("LIST", AccessibilityListPredicate);
261 AddToPredicateMap("LIST_ITEM", AccessibilityListItemPredicate);
262 AddToPredicateMap("MAIN", AccessibilityMainPredicate);
263 AddToPredicateMap("MEDIA", AccessibilityMediaPredicate);
264 AddToPredicateMap("RADIO", AccessibilityRadioButtonPredicate);
265 AddToPredicateMap("SECTION", SectionPredicate);
266 AddToPredicateMap("TABLE", AccessibilityTablePredicate);
267 AddToPredicateMap("TEXT_FIELD", AccessibilityTextfieldPredicate);
268 AddToPredicateMap("UNVISITED_LINK", AccessibilityUnvisitedLinkPredicate);
269 AddToPredicateMap("VISITED_LINK", AccessibilityVisitedLinkPredicate);
270 }
271
PredicateForSearchKey(const base::string16 & element_type)272 AccessibilityMatchPredicate PredicateForSearchKey(
273 const base::string16& element_type) {
274 InitSearchKeyToPredicateMapIfNeeded();
275 const auto& iter = g_search_key_to_predicate_map.Get().find(element_type);
276 if (iter != g_search_key_to_predicate_map.Get().end())
277 return iter->second;
278
279 // If we don't recognize the selector, return any element that a
280 // screen reader should navigate to.
281 return AllInterestingNodesPredicate;
282 }
283
284 // The element in the document for which we may be displaying an autofill popup.
285 int32_t g_element_hosting_autofill_popup_unique_id = -1;
286
287 // The element in the document that is the next element after
288 // |g_element_hosting_autofill_popup_unique_id|.
289 int32_t g_element_after_element_hosting_autofill_popup_unique_id = -1;
290
291 // Autofill popup will not be part of the |AXTree| that is sent by renderer.
292 // Hence, we need a proxy |AXNode| to represent the autofill popup.
293 BrowserAccessibility* g_autofill_popup_proxy_node = nullptr;
294 ui::AXNode* g_autofill_popup_proxy_node_ax_node = nullptr;
295
DeleteAutofillPopupProxy()296 void DeleteAutofillPopupProxy() {
297 if (g_autofill_popup_proxy_node) {
298 delete g_autofill_popup_proxy_node;
299 delete g_autofill_popup_proxy_node_ax_node;
300 g_autofill_popup_proxy_node = nullptr;
301 }
302 }
303
304 // The most common use of the EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
305 // API is to retrieve character bounds for one word at a time. There
306 // should never need to be a reason to return more than this many
307 // character bounding boxes at once. Set the limit much higher than needed
308 // but small enough to prevent wasting memory and cpu if abused.
309 const int kMaxCharacterBoundingBoxLen = 1024;
310
311 } // anonymous namespace
312
313 class WebContentsAccessibilityAndroid::Connector
314 : public RenderWidgetHostConnector {
315 public:
316 Connector(WebContents* web_contents,
317 WebContentsAccessibilityAndroid* accessibility);
318 ~Connector() override = default;
319
320 void DeleteEarly();
321
322 // RenderWidgetHostConnector:
323 void UpdateRenderProcessConnection(
324 RenderWidgetHostViewAndroid* old_rwhva,
325 RenderWidgetHostViewAndroid* new_rhwva) override;
326
327 private:
328 std::unique_ptr<WebContentsAccessibilityAndroid> accessibility_;
329 };
330
Connector(WebContents * web_contents,WebContentsAccessibilityAndroid * accessibility)331 WebContentsAccessibilityAndroid::Connector::Connector(
332 WebContents* web_contents,
333 WebContentsAccessibilityAndroid* accessibility)
334 : RenderWidgetHostConnector(web_contents), accessibility_(accessibility) {
335 Initialize();
336 }
337
DeleteEarly()338 void WebContentsAccessibilityAndroid::Connector::DeleteEarly() {
339 RenderWidgetHostConnector::DestroyEarly();
340 }
341
UpdateRenderProcessConnection(RenderWidgetHostViewAndroid * old_rwhva,RenderWidgetHostViewAndroid * new_rwhva)342 void WebContentsAccessibilityAndroid::Connector::UpdateRenderProcessConnection(
343 RenderWidgetHostViewAndroid* old_rwhva,
344 RenderWidgetHostViewAndroid* new_rwhva) {
345 if (old_rwhva)
346 old_rwhva->SetWebContentsAccessibility(nullptr);
347 if (new_rwhva)
348 new_rwhva->SetWebContentsAccessibility(accessibility_.get());
349 accessibility_->UpdateBrowserAccessibilityManager();
350 }
351
WebContentsAccessibilityAndroid(JNIEnv * env,const JavaParamRef<jobject> & obj,WebContents * web_contents)352 WebContentsAccessibilityAndroid::WebContentsAccessibilityAndroid(
353 JNIEnv* env,
354 const JavaParamRef<jobject>& obj,
355 WebContents* web_contents)
356 : java_ref_(env, obj),
357 web_contents_(static_cast<WebContentsImpl*>(web_contents)),
358 frame_info_initialized_(false),
359 use_zoom_for_dsf_enabled_(IsUseZoomForDSFEnabled()) {
360 // We must initialize this after weak_ptr_factory_ because it can result in
361 // calling UpdateBrowserAccessibilityManager() which accesses
362 // weak_ptr_factory_.
363 connector_ = new Connector(web_contents, this);
364
365 CollectStats();
366 }
367
~WebContentsAccessibilityAndroid()368 WebContentsAccessibilityAndroid::~WebContentsAccessibilityAndroid() {
369 JNIEnv* env = AttachCurrentThread();
370 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
371 if (obj.is_null())
372 return;
373
374 // Clean up autofill popup proxy node in case the popup was not dismissed.
375 DeleteAutofillPopupProxy();
376
377 Java_WebContentsAccessibilityImpl_onNativeObjectDestroyed(env, obj);
378 }
379
UpdateBrowserAccessibilityManager()380 void WebContentsAccessibilityAndroid::UpdateBrowserAccessibilityManager() {
381 auto* manager = GetRootBrowserAccessibilityManager();
382 if (manager)
383 manager->set_web_contents_accessibility(GetWeakPtr());
384 }
385
DeleteEarly(JNIEnv * env)386 void WebContentsAccessibilityAndroid::DeleteEarly(JNIEnv* env) {
387 connector_->DeleteEarly();
388 }
389
IsEnabled(JNIEnv * env,const JavaParamRef<jobject> & obj)390 jboolean WebContentsAccessibilityAndroid::IsEnabled(
391 JNIEnv* env,
392 const JavaParamRef<jobject>& obj) {
393 return GetRootBrowserAccessibilityManager() != nullptr;
394 }
395
Enable(JNIEnv * env,const JavaParamRef<jobject> & obj)396 void WebContentsAccessibilityAndroid::Enable(JNIEnv* env,
397 const JavaParamRef<jobject>& obj) {
398 BrowserAccessibilityStateImpl* accessibility_state =
399 BrowserAccessibilityStateImpl::GetInstance();
400 auto* manager = GetRootBrowserAccessibilityManager();
401
402 // First check if we already have a BrowserAccessibilityManager that
403 // that needs to be connected to this instance. This can happen if
404 // BAM creation precedes render view updates for the associated
405 // web contents.
406 if (manager) {
407 manager->set_web_contents_accessibility(GetWeakPtr());
408 return;
409 }
410
411 // Otherwise, enable accessibility globally unless it was
412 // explicitly disallowed by a command-line flag, then enable it for
413 // this WebContents if that succeeded.
414 accessibility_state->OnScreenReaderDetected();
415 if (accessibility_state->IsAccessibleBrowser())
416 web_contents_->AddAccessibilityMode(ui::kAXModeComplete);
417 }
418
ShouldRespectDisplayedPasswordText()419 bool WebContentsAccessibilityAndroid::ShouldRespectDisplayedPasswordText() {
420 JNIEnv* env = AttachCurrentThread();
421 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
422 if (obj.is_null())
423 return false;
424 return Java_WebContentsAccessibilityImpl_shouldRespectDisplayedPasswordText(
425 env, obj);
426 }
427
ShouldExposePasswordText()428 bool WebContentsAccessibilityAndroid::ShouldExposePasswordText() {
429 JNIEnv* env = AttachCurrentThread();
430 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
431 if (obj.is_null())
432 return false;
433 return Java_WebContentsAccessibilityImpl_shouldExposePasswordText(env, obj);
434 }
435
HandlePageLoaded(int32_t unique_id)436 void WebContentsAccessibilityAndroid::HandlePageLoaded(int32_t unique_id) {
437 JNIEnv* env = AttachCurrentThread();
438 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
439 if (obj.is_null())
440 return;
441 Java_WebContentsAccessibilityImpl_handlePageLoaded(env, obj, unique_id);
442 }
443
HandleContentChanged(int32_t unique_id)444 void WebContentsAccessibilityAndroid::HandleContentChanged(int32_t unique_id) {
445 JNIEnv* env = AttachCurrentThread();
446 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
447 if (obj.is_null())
448 return;
449 Java_WebContentsAccessibilityImpl_handleContentChanged(env, obj, unique_id);
450 }
451
HandleFocusChanged(int32_t unique_id)452 void WebContentsAccessibilityAndroid::HandleFocusChanged(int32_t unique_id) {
453 JNIEnv* env = AttachCurrentThread();
454 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
455 if (obj.is_null())
456 return;
457 Java_WebContentsAccessibilityImpl_handleFocusChanged(env, obj, unique_id);
458 }
459
HandleCheckStateChanged(int32_t unique_id)460 void WebContentsAccessibilityAndroid::HandleCheckStateChanged(
461 int32_t unique_id) {
462 JNIEnv* env = AttachCurrentThread();
463 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
464 if (obj.is_null())
465 return;
466 Java_WebContentsAccessibilityImpl_handleCheckStateChanged(env, obj,
467 unique_id);
468 }
469
HandleClicked(int32_t unique_id)470 void WebContentsAccessibilityAndroid::HandleClicked(int32_t unique_id) {
471 JNIEnv* env = AttachCurrentThread();
472 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
473 if (obj.is_null())
474 return;
475 Java_WebContentsAccessibilityImpl_handleClicked(env, obj, unique_id);
476 }
477
HandleScrollPositionChanged(int32_t unique_id)478 void WebContentsAccessibilityAndroid::HandleScrollPositionChanged(
479 int32_t unique_id) {
480 JNIEnv* env = AttachCurrentThread();
481 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
482 if (obj.is_null())
483 return;
484 Java_WebContentsAccessibilityImpl_handleScrollPositionChanged(env, obj,
485 unique_id);
486 }
487
HandleScrolledToAnchor(int32_t unique_id)488 void WebContentsAccessibilityAndroid::HandleScrolledToAnchor(
489 int32_t unique_id) {
490 JNIEnv* env = AttachCurrentThread();
491 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
492 if (obj.is_null())
493 return;
494 Java_WebContentsAccessibilityImpl_handleScrolledToAnchor(env, obj, unique_id);
495 }
496
AnnounceLiveRegionText(const base::string16 & text)497 void WebContentsAccessibilityAndroid::AnnounceLiveRegionText(
498 const base::string16& text) {
499 JNIEnv* env = AttachCurrentThread();
500 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
501 if (obj.is_null())
502 return;
503 Java_WebContentsAccessibilityImpl_announceLiveRegionText(
504 env, obj, base::android::ConvertUTF16ToJavaString(env, text));
505 }
506
HandleTextSelectionChanged(int32_t unique_id)507 void WebContentsAccessibilityAndroid::HandleTextSelectionChanged(
508 int32_t unique_id) {
509 JNIEnv* env = AttachCurrentThread();
510 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
511 if (obj.is_null())
512 return;
513 Java_WebContentsAccessibilityImpl_handleTextSelectionChanged(env, obj,
514 unique_id);
515 }
516
HandleEditableTextChanged(int32_t unique_id)517 void WebContentsAccessibilityAndroid::HandleEditableTextChanged(
518 int32_t unique_id) {
519 JNIEnv* env = AttachCurrentThread();
520 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
521 if (obj.is_null())
522 return;
523 Java_WebContentsAccessibilityImpl_handleEditableTextChanged(env, obj,
524 unique_id);
525 }
526
HandleSliderChanged(int32_t unique_id)527 void WebContentsAccessibilityAndroid::HandleSliderChanged(int32_t unique_id) {
528 JNIEnv* env = AttachCurrentThread();
529 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
530 if (obj.is_null())
531 return;
532 Java_WebContentsAccessibilityImpl_handleSliderChanged(env, obj, unique_id);
533 }
534
SendDelayedWindowContentChangedEvent()535 void WebContentsAccessibilityAndroid::SendDelayedWindowContentChangedEvent() {
536 JNIEnv* env = AttachCurrentThread();
537 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
538 if (obj.is_null())
539 return;
540 Java_WebContentsAccessibilityImpl_sendDelayedWindowContentChangedEvent(env,
541 obj);
542 }
543
HandleHover(int32_t unique_id)544 void WebContentsAccessibilityAndroid::HandleHover(int32_t unique_id) {
545 JNIEnv* env = AttachCurrentThread();
546 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
547 if (obj.is_null())
548 return;
549 Java_WebContentsAccessibilityImpl_handleHover(env, obj, unique_id);
550 }
551
OnHoverEvent(const ui::MotionEventAndroid & event)552 bool WebContentsAccessibilityAndroid::OnHoverEvent(
553 const ui::MotionEventAndroid& event) {
554 JNIEnv* env = AttachCurrentThread();
555 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
556 if (obj.is_null())
557 return false;
558
559 if (!Java_WebContentsAccessibilityImpl_onHoverEvent(
560 env, obj,
561 ui::MotionEventAndroid::GetAndroidAction(event.GetAction())))
562 return false;
563
564 // |HitTest| sends an IPC to the render process to do the hit testing.
565 // The response is handled by HandleHover when it returns.
566 // Hover event was consumed by accessibility by now. Return true to
567 // stop the event from proceeding.
568 if (event.GetAction() != ui::MotionEvent::Action::HOVER_EXIT &&
569 GetRootBrowserAccessibilityManager()) {
570 gfx::PointF point = event.GetPointPix();
571 point.Scale(1 / page_scale_);
572 GetRootBrowserAccessibilityManager()->HitTest(gfx::ToFlooredPoint(point));
573 }
574 return true;
575 }
576
HandleNavigate()577 void WebContentsAccessibilityAndroid::HandleNavigate() {
578 JNIEnv* env = AttachCurrentThread();
579 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
580 if (obj.is_null())
581 return;
582 Java_WebContentsAccessibilityImpl_handleNavigate(env, obj);
583 }
584
ClearNodeInfoCacheForGivenId(int32_t unique_id)585 void WebContentsAccessibilityAndroid::ClearNodeInfoCacheForGivenId(
586 int32_t unique_id) {
587 JNIEnv* env = AttachCurrentThread();
588 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
589 if (obj.is_null())
590 return;
591
592 Java_WebContentsAccessibilityImpl_clearNodeInfoCacheForGivenId(env, obj,
593 unique_id);
594 }
595
596 base::android::ScopedJavaLocalRef<jstring>
GetSupportedHtmlElementTypes(JNIEnv * env,const JavaParamRef<jobject> & obj)597 WebContentsAccessibilityAndroid::GetSupportedHtmlElementTypes(
598 JNIEnv* env,
599 const JavaParamRef<jobject>& obj) {
600 InitSearchKeyToPredicateMapIfNeeded();
601 return base::android::ConvertUTF16ToJavaString(env, g_all_search_keys.Get());
602 }
603
GetRootId(JNIEnv * env,const JavaParamRef<jobject> & obj)604 jint WebContentsAccessibilityAndroid::GetRootId(
605 JNIEnv* env,
606 const JavaParamRef<jobject>& obj) {
607 if (auto* root_manager = GetRootBrowserAccessibilityManager()) {
608 auto* root =
609 static_cast<BrowserAccessibilityAndroid*>(root_manager->GetRoot());
610 if (root)
611 return static_cast<jint>(root->unique_id());
612 }
613 return -1;
614 }
615
IsNodeValid(JNIEnv * env,const JavaParamRef<jobject> & obj,jint unique_id)616 jboolean WebContentsAccessibilityAndroid::IsNodeValid(
617 JNIEnv* env,
618 const JavaParamRef<jobject>& obj,
619 jint unique_id) {
620 return GetAXFromUniqueID(unique_id) != NULL;
621 }
622
HitTest(JNIEnv * env,const JavaParamRef<jobject> & obj,jint x,jint y)623 void WebContentsAccessibilityAndroid::HitTest(JNIEnv* env,
624 const JavaParamRef<jobject>& obj,
625 jint x,
626 jint y) {
627 if (auto* root_manager = GetRootBrowserAccessibilityManager())
628 root_manager->HitTest(gfx::Point(x, y));
629 }
630
IsEditableText(JNIEnv * env,const JavaParamRef<jobject> & obj,jint unique_id)631 jboolean WebContentsAccessibilityAndroid::IsEditableText(
632 JNIEnv* env,
633 const JavaParamRef<jobject>& obj,
634 jint unique_id) {
635 BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id);
636 if (!node)
637 return false;
638
639 return node->IsTextField();
640 }
641
IsFocused(JNIEnv * env,const JavaParamRef<jobject> & obj,jint unique_id)642 jboolean WebContentsAccessibilityAndroid::IsFocused(
643 JNIEnv* env,
644 const JavaParamRef<jobject>& obj,
645 jint unique_id) {
646 BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id);
647 if (!node)
648 return false;
649
650 return node->IsFocused();
651 }
652
GetEditableTextSelectionStart(JNIEnv * env,const JavaParamRef<jobject> & obj,jint unique_id)653 jint WebContentsAccessibilityAndroid::GetEditableTextSelectionStart(
654 JNIEnv* env,
655 const JavaParamRef<jobject>& obj,
656 jint unique_id) {
657 BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id);
658 if (!node)
659 return false;
660
661 return node->GetSelectionStart();
662 }
663
GetEditableTextSelectionEnd(JNIEnv * env,const JavaParamRef<jobject> & obj,jint unique_id)664 jint WebContentsAccessibilityAndroid::GetEditableTextSelectionEnd(
665 JNIEnv* env,
666 const JavaParamRef<jobject>& obj,
667 jint unique_id) {
668 BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id);
669 if (!node)
670 return false;
671
672 return node->GetSelectionEnd();
673 }
674
ActualUnignoredChildCount(const ui::AXNode * node)675 static size_t ActualUnignoredChildCount(const ui::AXNode* node) {
676 size_t count = 0;
677 for (const ui::AXNode* child : node->children()) {
678 if (child->IsIgnored()) {
679 count += ActualUnignoredChildCount(child);
680 } else {
681 ++count;
682 }
683 }
684 return count;
685 }
686
UpdateAccessibilityNodeInfoBoundsRect(JNIEnv * env,const JavaParamRef<jobject> & obj,const JavaParamRef<jobject> & info,jint unique_id,BrowserAccessibilityAndroid * node)687 void WebContentsAccessibilityAndroid::UpdateAccessibilityNodeInfoBoundsRect(
688 JNIEnv* env,
689 const JavaParamRef<jobject>& obj,
690 const JavaParamRef<jobject>& info,
691 jint unique_id,
692 BrowserAccessibilityAndroid* node) {
693 auto* root_manager = GetRootBrowserAccessibilityManager();
694 if (!root_manager)
695 return;
696
697 float dip_scale =
698 use_zoom_for_dsf_enabled_ ? 1 / root_manager->device_scale_factor() : 1.0;
699 gfx::Rect absolute_rect = gfx::ScaleToEnclosingRect(
700 node->GetUnclippedRootFrameBoundsRect(), dip_scale, dip_scale);
701 gfx::Rect parent_relative_rect = absolute_rect;
702 bool is_root = node->PlatformGetParent() == nullptr;
703 if (!is_root) {
704 gfx::Rect parent_rect = gfx::ScaleToEnclosingRect(
705 node->PlatformGetParent()->GetUnclippedRootFrameBoundsRect(), dip_scale,
706 dip_scale);
707 parent_relative_rect.Offset(-parent_rect.OffsetFromOrigin());
708 }
709 Java_WebContentsAccessibilityImpl_setAccessibilityNodeInfoLocation(
710 env, obj, info, unique_id, absolute_rect.x(), absolute_rect.y(),
711 parent_relative_rect.x(), parent_relative_rect.y(), absolute_rect.width(),
712 absolute_rect.height(), is_root);
713 }
714
UpdateCachedAccessibilityNodeInfo(JNIEnv * env,const JavaParamRef<jobject> & obj,const JavaParamRef<jobject> & info,jint unique_id)715 jboolean WebContentsAccessibilityAndroid::UpdateCachedAccessibilityNodeInfo(
716 JNIEnv* env,
717 const JavaParamRef<jobject>& obj,
718 const JavaParamRef<jobject>& info,
719 jint unique_id) {
720 auto* root_manager = GetRootBrowserAccessibilityManager();
721 if (!root_manager)
722 return false;
723
724 BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id);
725 if (!node)
726 return false;
727
728 // Update cached nodes by providing new enclosing Rects
729 UpdateAccessibilityNodeInfoBoundsRect(env, obj, info, unique_id, node);
730
731 return true;
732 }
733
PopulateAccessibilityNodeInfo(JNIEnv * env,const JavaParamRef<jobject> & obj,const JavaParamRef<jobject> & info,jint unique_id)734 jboolean WebContentsAccessibilityAndroid::PopulateAccessibilityNodeInfo(
735 JNIEnv* env,
736 const JavaParamRef<jobject>& obj,
737 const JavaParamRef<jobject>& info,
738 jint unique_id) {
739 if (!GetRootBrowserAccessibilityManager())
740 return false;
741
742 BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id);
743 if (!node)
744 return false;
745
746 bool is_root = node->PlatformGetParent() == nullptr;
747 if (!is_root) {
748 auto* android_node =
749 static_cast<BrowserAccessibilityAndroid*>(node->PlatformGetParent());
750 Java_WebContentsAccessibilityImpl_setAccessibilityNodeInfoParent(
751 env, obj, info, android_node->unique_id());
752 }
753 for (BrowserAccessibility::PlatformChildIterator it =
754 node->PlatformChildrenBegin();
755 it != node->PlatformChildrenEnd(); ++it) {
756 auto* android_node = static_cast<BrowserAccessibilityAndroid*>(it.get());
757 Java_WebContentsAccessibilityImpl_addAccessibilityNodeInfoChild(
758 env, obj, info, android_node->unique_id());
759 }
760 Java_WebContentsAccessibilityImpl_setAccessibilityNodeInfoBooleanAttributes(
761 env, obj, info, unique_id, node->IsReportingCheckable(),
762 node->IsChecked(), node->IsClickable(), node->IsContentInvalid(),
763 node->IsEnabled(), node->IsFocusable(), node->IsFocused(),
764 node->HasImage(), node->IsPasswordField(), node->IsScrollable(),
765 node->IsSelected(), node->IsVisibleToUser());
766 Java_WebContentsAccessibilityImpl_addAccessibilityNodeInfoActions(
767 env, obj, info, unique_id, node->CanScrollForward(),
768 node->CanScrollBackward(), node->CanScrollUp(), node->CanScrollDown(),
769 node->CanScrollLeft(), node->CanScrollRight(), node->IsClickable(),
770 node->IsTextField(), node->IsEnabled(), node->IsFocusable(),
771 node->IsFocused(), node->IsCollapsed(), node->IsExpanded(),
772 node->HasNonEmptyValue(), !node->GetInnerText().empty(),
773 node->IsSeekControl(), node->IsFormDescendant());
774
775 Java_WebContentsAccessibilityImpl_setAccessibilityNodeInfoBaseAttributes(
776 env, obj, info, is_root,
777 base::android::ConvertUTF8ToJavaString(env, node->GetClassName()),
778 base::android::ConvertUTF8ToJavaString(env, node->GetRoleString()),
779 base::android::ConvertUTF16ToJavaString(env, node->GetRoleDescription()),
780 base::android::ConvertUTF16ToJavaString(env, node->GetHint()),
781 base::android::ConvertUTF16ToJavaString(env, node->GetTargetUrl()));
782
783 ScopedJavaLocalRef<jintArray> suggestion_starts_java;
784 ScopedJavaLocalRef<jintArray> suggestion_ends_java;
785 ScopedJavaLocalRef<jobjectArray> suggestion_text_java;
786 std::vector<int> suggestion_starts;
787 std::vector<int> suggestion_ends;
788 node->GetSuggestions(&suggestion_starts, &suggestion_ends);
789 if (suggestion_starts.size() && suggestion_ends.size()) {
790 suggestion_starts_java = base::android::ToJavaIntArray(
791 env, suggestion_starts.data(), suggestion_starts.size());
792 suggestion_ends_java = base::android::ToJavaIntArray(
793 env, suggestion_ends.data(), suggestion_ends.size());
794
795 // Currently we don't retrieve the text of each suggestion, so
796 // store a blank string for now.
797 std::vector<std::string> suggestion_text(suggestion_starts.size());
798 suggestion_text_java =
799 base::android::ToJavaArrayOfStrings(env, suggestion_text);
800 }
801
802 Java_WebContentsAccessibilityImpl_setAccessibilityNodeInfoText(
803 env, obj, info,
804 base::android::ConvertUTF16ToJavaString(env, node->GetInnerText()),
805 node->IsLink(), node->IsTextField(),
806 base::android::ConvertUTF16ToJavaString(
807 env, node->GetInheritedString16Attribute(
808 ax::mojom::StringAttribute::kLanguage)),
809 suggestion_starts_java, suggestion_ends_java, suggestion_text_java,
810 base::android::ConvertUTF16ToJavaString(env,
811 node->GetStateDescription()));
812
813 base::string16 element_id;
814 if (node->GetHtmlAttribute("id", &element_id)) {
815 Java_WebContentsAccessibilityImpl_setAccessibilityNodeInfoViewIdResourceName(
816 env, obj, info,
817 base::android::ConvertUTF16ToJavaString(env, element_id));
818 }
819
820 UpdateAccessibilityNodeInfoBoundsRect(env, obj, info, unique_id, node);
821
822 Java_WebContentsAccessibilityImpl_setAccessibilityNodeInfoAttributes(
823 env, obj, info, node->CanOpenPopup(), node->IsDismissable(),
824 node->IsMultiLine(), node->AndroidInputType(),
825 node->AndroidLiveRegionType(),
826 base::android::ConvertUTF16ToJavaString(
827 env, node->GetContentInvalidErrorMessage()));
828
829 Java_WebContentsAccessibilityImpl_setAccessibilityNodeInfoOAttributes(
830 env, obj, info, node->HasCharacterLocations(),
831 base::android::ConvertUTF16ToJavaString(env, node->GetHint()));
832
833 if (node->IsCollection()) {
834 Java_WebContentsAccessibilityImpl_setAccessibilityNodeInfoCollectionInfo(
835 env, obj, info, node->RowCount(), node->ColumnCount(),
836 node->IsHierarchical());
837 }
838 if (node->IsCollectionItem() || node->IsHeading()) {
839 Java_WebContentsAccessibilityImpl_setAccessibilityNodeInfoCollectionItemInfo(
840 env, obj, info, node->RowIndex(), node->RowSpan(), node->ColumnIndex(),
841 node->ColumnSpan(), node->IsHeading());
842 }
843 if (node->GetData().IsRangeValueSupported()) {
844 Java_WebContentsAccessibilityImpl_setAccessibilityNodeInfoRangeInfo(
845 env, obj, info, node->AndroidRangeType(), node->RangeMin(),
846 node->RangeMax(), node->RangeCurrentValue());
847 }
848
849 if (ui::IsDialog(node->GetRole())) {
850 Java_WebContentsAccessibilityImpl_setAccessibilityNodeInfoPaneTitle(
851 env, obj, info,
852 base::android::ConvertUTF16ToJavaString(env, node->GetInnerText()));
853 }
854
855 if (node->IsTextField()) {
856 Java_WebContentsAccessibilityImpl_setAccessibilityNodeInfoSelectionAttrs(
857 env, obj, info, node->GetSelectionStart(), node->GetSelectionEnd());
858 }
859
860 return true;
861 }
862
PopulateAccessibilityEvent(JNIEnv * env,const JavaParamRef<jobject> & obj,const JavaParamRef<jobject> & event,jint unique_id,jint event_type)863 jboolean WebContentsAccessibilityAndroid::PopulateAccessibilityEvent(
864 JNIEnv* env,
865 const JavaParamRef<jobject>& obj,
866 const JavaParamRef<jobject>& event,
867 jint unique_id,
868 jint event_type) {
869 BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id);
870 if (!node)
871 return false;
872
873 Java_WebContentsAccessibilityImpl_setAccessibilityEventBooleanAttributes(
874 env, obj, event, node->IsChecked(), node->IsEnabled(),
875 node->IsPasswordField(), node->IsScrollable());
876 Java_WebContentsAccessibilityImpl_setAccessibilityEventClassName(
877 env, obj, event,
878 base::android::ConvertUTF8ToJavaString(env, node->GetClassName()));
879 Java_WebContentsAccessibilityImpl_setAccessibilityEventListAttributes(
880 env, obj, event, node->GetItemIndex(), node->GetItemCount());
881 Java_WebContentsAccessibilityImpl_setAccessibilityEventScrollAttributes(
882 env, obj, event, node->GetScrollX(), node->GetScrollY(),
883 node->GetMaxScrollX(), node->GetMaxScrollY());
884
885 switch (event_type) {
886 case ANDROID_ACCESSIBILITY_EVENT_TEXT_CHANGED: {
887 base::string16 before_text = node->GetTextChangeBeforeText();
888 base::string16 text = node->GetInnerText();
889 Java_WebContentsAccessibilityImpl_setAccessibilityEventTextChangedAttrs(
890 env, obj, event, node->GetTextChangeFromIndex(),
891 node->GetTextChangeAddedCount(), node->GetTextChangeRemovedCount(),
892 base::android::ConvertUTF16ToJavaString(env, before_text),
893 base::android::ConvertUTF16ToJavaString(env, text));
894 break;
895 }
896 case ANDROID_ACCESSIBILITY_EVENT_TEXT_SELECTION_CHANGED: {
897 base::string16 text = node->GetInnerText();
898 Java_WebContentsAccessibilityImpl_setAccessibilityEventSelectionAttrs(
899 env, obj, event, node->GetSelectionStart(), node->GetSelectionEnd(),
900 node->GetEditableTextLength(),
901 base::android::ConvertUTF16ToJavaString(env, text));
902 break;
903 }
904 default:
905 break;
906 }
907
908 return true;
909 }
910
Click(JNIEnv * env,const JavaParamRef<jobject> & obj,jint unique_id)911 void WebContentsAccessibilityAndroid::Click(JNIEnv* env,
912 const JavaParamRef<jobject>& obj,
913 jint unique_id) {
914 BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id);
915
916 // If it's a heading consisting of only a link, click the link.
917 if (node->IsHeadingLink()) {
918 node = static_cast<BrowserAccessibilityAndroid*>(
919 node->InternalChildrenBegin().get());
920 }
921
922 if (node)
923 node->manager()->DoDefaultAction(*node);
924 }
925
Focus(JNIEnv * env,const JavaParamRef<jobject> & obj,jint unique_id)926 void WebContentsAccessibilityAndroid::Focus(JNIEnv* env,
927 const JavaParamRef<jobject>& obj,
928 jint unique_id) {
929 BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id);
930 if (node)
931 node->manager()->SetFocus(*node);
932 }
933
Blur(JNIEnv * env,const JavaParamRef<jobject> & obj)934 void WebContentsAccessibilityAndroid::Blur(JNIEnv* env,
935 const JavaParamRef<jobject>& obj) {
936 if (auto* root_manager = GetRootBrowserAccessibilityManager())
937 root_manager->SetFocus(*root_manager->GetRoot());
938 }
939
ScrollToMakeNodeVisible(JNIEnv * env,const JavaParamRef<jobject> & obj,jint unique_id)940 void WebContentsAccessibilityAndroid::ScrollToMakeNodeVisible(
941 JNIEnv* env,
942 const JavaParamRef<jobject>& obj,
943 jint unique_id) {
944 BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id);
945 if (node) {
946 node->manager()->ScrollToMakeVisible(
947 *node, gfx::Rect(node->GetClippedFrameBoundsRect().size()));
948 }
949 }
950
SetTextFieldValue(JNIEnv * env,const JavaParamRef<jobject> & obj,jint unique_id,const JavaParamRef<jstring> & value)951 void WebContentsAccessibilityAndroid::SetTextFieldValue(
952 JNIEnv* env,
953 const JavaParamRef<jobject>& obj,
954 jint unique_id,
955 const JavaParamRef<jstring>& value) {
956 BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id);
957 if (node) {
958 node->manager()->SetValue(
959 *node, base::android::ConvertJavaStringToUTF8(env, value));
960 }
961 }
962
SetSelection(JNIEnv * env,const JavaParamRef<jobject> & obj,jint unique_id,jint start,jint end)963 void WebContentsAccessibilityAndroid::SetSelection(
964 JNIEnv* env,
965 const JavaParamRef<jobject>& obj,
966 jint unique_id,
967 jint start,
968 jint end) {
969 using AXPlatformPositionInstance =
970 BrowserAccessibilityPosition::AXPositionInstance;
971 using AXPlatformRange = ui::AXRange<AXPlatformPositionInstance::element_type>;
972
973 BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id);
974 if (node) {
975 node->manager()->SetSelection(
976 AXPlatformRange(node->CreatePositionForSelectionAt(start),
977 node->CreatePositionForSelectionAt(end)));
978 }
979 }
980
AdjustSlider(JNIEnv * env,const JavaParamRef<jobject> & obj,jint unique_id,jboolean increment)981 jboolean WebContentsAccessibilityAndroid::AdjustSlider(
982 JNIEnv* env,
983 const JavaParamRef<jobject>& obj,
984 jint unique_id,
985 jboolean increment) {
986 BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id);
987 if (!node)
988 return false;
989
990 BrowserAccessibilityAndroid* android_node =
991 static_cast<BrowserAccessibilityAndroid*>(node);
992
993 if (!android_node->IsSlider() || !android_node->IsEnabled())
994 return false;
995
996 float value =
997 node->GetFloatAttribute(ax::mojom::FloatAttribute::kValueForRange);
998 float min =
999 node->GetFloatAttribute(ax::mojom::FloatAttribute::kMinValueForRange);
1000 float max =
1001 node->GetFloatAttribute(ax::mojom::FloatAttribute::kMaxValueForRange);
1002 if (max <= min)
1003 return false;
1004
1005 // To behave similarly to an Android SeekBar, move by an increment of
1006 // approximately 5%.
1007 float original_value = value;
1008 float delta = (max - min) / 20.0f;
1009 // Slider does not move if the delta value is less than 1.
1010 delta = ((delta < 1) ? 1 : delta);
1011 value += (increment ? delta : -delta);
1012 value = base::ClampToRange(value, min, max);
1013 if (value != original_value) {
1014 node->manager()->SetValue(*node, base::NumberToString(value));
1015 return true;
1016 }
1017 return false;
1018 }
1019
ShowContextMenu(JNIEnv * env,const JavaParamRef<jobject> & obj,jint unique_id)1020 void WebContentsAccessibilityAndroid::ShowContextMenu(
1021 JNIEnv* env,
1022 const JavaParamRef<jobject>& obj,
1023 jint unique_id) {
1024 BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id);
1025 if (node)
1026 node->manager()->ShowContextMenu(*node);
1027 }
1028
FindElementType(JNIEnv * env,const JavaParamRef<jobject> & obj,jint start_id,const JavaParamRef<jstring> & element_type_str,jboolean forwards)1029 jint WebContentsAccessibilityAndroid::FindElementType(
1030 JNIEnv* env,
1031 const JavaParamRef<jobject>& obj,
1032 jint start_id,
1033 const JavaParamRef<jstring>& element_type_str,
1034 jboolean forwards) {
1035 BrowserAccessibilityAndroid* start_node = GetAXFromUniqueID(start_id);
1036 if (!start_node)
1037 return 0;
1038
1039 auto* root_manager = GetRootBrowserAccessibilityManager();
1040 if (!root_manager)
1041 return 0;
1042
1043 BrowserAccessibility* root = root_manager->GetRoot();
1044 if (!root)
1045 return 0;
1046
1047 AccessibilityMatchPredicate predicate = PredicateForSearchKey(
1048 base::android::ConvertJavaStringToUTF16(env, element_type_str));
1049
1050 OneShotAccessibilityTreeSearch tree_search(root);
1051 tree_search.SetStartNode(start_node);
1052 tree_search.SetDirection(forwards
1053 ? OneShotAccessibilityTreeSearch::FORWARDS
1054 : OneShotAccessibilityTreeSearch::BACKWARDS);
1055 tree_search.SetResultLimit(1);
1056 tree_search.SetImmediateDescendantsOnly(false);
1057 // SetCanWrapToLastElement needs to be set as true after talkback pushes its
1058 // corresponding change for b/29103330.
1059 tree_search.SetCanWrapToLastElement(false);
1060 tree_search.SetOnscreenOnly(false);
1061 tree_search.AddPredicate(predicate);
1062
1063 if (tree_search.CountMatches() == 0)
1064 return 0;
1065
1066 auto* android_node =
1067 static_cast<BrowserAccessibilityAndroid*>(tree_search.GetMatchAtIndex(0));
1068 int32_t element_id = android_node->unique_id();
1069
1070 // Navigate forwards to the autofill popup's proxy node if focus is currently
1071 // on the element hosting the autofill popup. Once within the popup, a back
1072 // press will navigate back to the element hosting the popup. If user swipes
1073 // past last suggestion in the popup, or swipes left from the first suggestion
1074 // in the popup, we will navigate to the element that is the next element in
1075 // the document after the element hosting the popup.
1076 if (forwards && start_id == g_element_hosting_autofill_popup_unique_id &&
1077 g_autofill_popup_proxy_node) {
1078 g_element_after_element_hosting_autofill_popup_unique_id = element_id;
1079 auto* proxy_android_node =
1080 static_cast<BrowserAccessibilityAndroid*>(g_autofill_popup_proxy_node);
1081 return proxy_android_node->unique_id();
1082 }
1083
1084 return element_id;
1085 }
1086
NextAtGranularity(JNIEnv * env,const JavaParamRef<jobject> & obj,jint granularity,jboolean extend_selection,jint unique_id,jint cursor_index)1087 jboolean WebContentsAccessibilityAndroid::NextAtGranularity(
1088 JNIEnv* env,
1089 const JavaParamRef<jobject>& obj,
1090 jint granularity,
1091 jboolean extend_selection,
1092 jint unique_id,
1093 jint cursor_index) {
1094 auto* root_manager = GetRootBrowserAccessibilityManager();
1095 if (!root_manager)
1096 return false;
1097
1098 BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id);
1099 if (!node)
1100 return false;
1101
1102 jint start_index = -1;
1103 int end_index = -1;
1104 if (root_manager->NextAtGranularity(granularity, cursor_index, node,
1105 &start_index, &end_index)) {
1106 base::string16 text = node->GetInnerText();
1107 Java_WebContentsAccessibilityImpl_finishGranularityMoveNext(
1108 env, obj, base::android::ConvertUTF16ToJavaString(env, text),
1109 extend_selection, start_index, end_index);
1110 return true;
1111 }
1112 return false;
1113 }
1114
GetTextLength(JNIEnv * env,const JavaParamRef<jobject> & obj,jint unique_id)1115 jint WebContentsAccessibilityAndroid::GetTextLength(
1116 JNIEnv* env,
1117 const JavaParamRef<jobject>& obj,
1118 jint unique_id) {
1119 BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id);
1120 if (!node)
1121 return -1;
1122 base::string16 text = node->GetInnerText();
1123 return text.size();
1124 }
1125
AddSpellingErrorForTesting(JNIEnv * env,const JavaParamRef<jobject> & obj,jint unique_id,jint start_offset,jint end_offset)1126 void WebContentsAccessibilityAndroid::AddSpellingErrorForTesting(
1127 JNIEnv* env,
1128 const JavaParamRef<jobject>& obj,
1129 jint unique_id,
1130 jint start_offset,
1131 jint end_offset) {
1132 BrowserAccessibility* node = GetAXFromUniqueID(unique_id);
1133 CHECK(node);
1134
1135 while (node->GetRole() != ax::mojom::Role::kStaticText &&
1136 node->InternalChildCount() > 0) {
1137 node = node->InternalChildrenBegin().get();
1138 }
1139
1140 CHECK(node->GetRole() == ax::mojom::Role::kStaticText);
1141 base::string16 text = node->GetInnerText();
1142 CHECK_LT(start_offset, static_cast<int>(text.size()));
1143 CHECK_LE(end_offset, static_cast<int>(text.size()));
1144
1145 ui::AXNodeData data = node->GetData();
1146 data.AddIntListAttribute(ax::mojom::IntListAttribute::kMarkerStarts,
1147 {start_offset});
1148 data.AddIntListAttribute(ax::mojom::IntListAttribute::kMarkerEnds,
1149 {end_offset});
1150 data.AddIntListAttribute(
1151 ax::mojom::IntListAttribute::kMarkerTypes,
1152 {static_cast<int>(ax::mojom::MarkerType::kSuggestion)});
1153 node->node()->SetData(data);
1154 }
1155
PreviousAtGranularity(JNIEnv * env,const JavaParamRef<jobject> & obj,jint granularity,jboolean extend_selection,jint unique_id,jint cursor_index)1156 jboolean WebContentsAccessibilityAndroid::PreviousAtGranularity(
1157 JNIEnv* env,
1158 const JavaParamRef<jobject>& obj,
1159 jint granularity,
1160 jboolean extend_selection,
1161 jint unique_id,
1162 jint cursor_index) {
1163 auto* root_manager = GetRootBrowserAccessibilityManager();
1164 if (!root_manager)
1165 return false;
1166
1167 BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id);
1168 if (!node)
1169 return false;
1170
1171 jint start_index = -1;
1172 int end_index = -1;
1173 if (root_manager->PreviousAtGranularity(granularity, cursor_index, node,
1174 &start_index, &end_index)) {
1175 Java_WebContentsAccessibilityImpl_finishGranularityMovePrevious(
1176 env, obj,
1177 base::android::ConvertUTF16ToJavaString(env, node->GetInnerText()),
1178 extend_selection, start_index, end_index);
1179 return true;
1180 }
1181 return false;
1182 }
1183
MoveAccessibilityFocus(JNIEnv * env,const JavaParamRef<jobject> & obj,jint old_unique_id,jint new_unique_id)1184 void WebContentsAccessibilityAndroid::MoveAccessibilityFocus(
1185 JNIEnv* env,
1186 const JavaParamRef<jobject>& obj,
1187 jint old_unique_id,
1188 jint new_unique_id) {
1189 BrowserAccessibilityAndroid* old_node = GetAXFromUniqueID(old_unique_id);
1190 if (old_node)
1191 old_node->manager()->ClearAccessibilityFocus(*old_node);
1192
1193 BrowserAccessibilityAndroid* node = GetAXFromUniqueID(new_unique_id);
1194 if (!node)
1195 return;
1196 node->manager()->SetAccessibilityFocus(*node);
1197
1198 // When Android sets accessibility focus to a node, we load inline text
1199 // boxes for that node so that subsequent requests for character bounding
1200 // boxes will succeed. However, don't do that for the root of the tree,
1201 // as that will result in loading inline text boxes for the whole tree.
1202 if (node != node->manager()->GetRoot())
1203 node->manager()->LoadInlineTextBoxes(*node);
1204 }
1205
IsSlider(JNIEnv * env,const JavaParamRef<jobject> & obj,jint unique_id)1206 bool WebContentsAccessibilityAndroid::IsSlider(JNIEnv* env,
1207 const JavaParamRef<jobject>& obj,
1208 jint unique_id) {
1209 BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id);
1210 if (!node)
1211 return false;
1212
1213 return node->GetRole() == ax::mojom::Role::kSlider;
1214 }
1215
OnAutofillPopupDisplayed(JNIEnv * env,const JavaParamRef<jobject> & obj)1216 void WebContentsAccessibilityAndroid::OnAutofillPopupDisplayed(
1217 JNIEnv* env,
1218 const JavaParamRef<jobject>& obj) {
1219 auto* root_manager = GetRootBrowserAccessibilityManager();
1220 if (!root_manager ||
1221 !base::FeatureList::IsEnabled(features::kAndroidAutofillAccessibility))
1222 return;
1223
1224 BrowserAccessibility* current_focus = root_manager->GetFocus();
1225 if (current_focus == nullptr) {
1226 return;
1227 }
1228
1229 DeleteAutofillPopupProxy();
1230
1231 g_autofill_popup_proxy_node = BrowserAccessibility::Create();
1232 g_autofill_popup_proxy_node_ax_node = new ui::AXNode(nullptr, nullptr, -1, 0);
1233 ui::AXNodeData ax_node_data;
1234 ax_node_data.role = ax::mojom::Role::kMenu;
1235 ax_node_data.SetName("Autofill");
1236 ax_node_data.SetRestriction(ax::mojom::Restriction::kReadOnly);
1237 ax_node_data.AddState(ax::mojom::State::kFocusable);
1238 ax_node_data.AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, false);
1239 g_autofill_popup_proxy_node_ax_node->SetData(ax_node_data);
1240 g_autofill_popup_proxy_node->Init(root_manager,
1241 g_autofill_popup_proxy_node_ax_node);
1242
1243 auto* android_node = static_cast<BrowserAccessibilityAndroid*>(current_focus);
1244
1245 g_element_hosting_autofill_popup_unique_id = android_node->unique_id();
1246 }
1247
OnAutofillPopupDismissed(JNIEnv * env,const JavaParamRef<jobject> & obj)1248 void WebContentsAccessibilityAndroid::OnAutofillPopupDismissed(
1249 JNIEnv* env,
1250 const JavaParamRef<jobject>& obj) {
1251 g_element_hosting_autofill_popup_unique_id = -1;
1252 g_element_after_element_hosting_autofill_popup_unique_id = -1;
1253 DeleteAutofillPopupProxy();
1254 }
1255
1256 jint WebContentsAccessibilityAndroid::
GetIdForElementAfterElementHostingAutofillPopup(JNIEnv * env,const JavaParamRef<jobject> & obj)1257 GetIdForElementAfterElementHostingAutofillPopup(
1258 JNIEnv* env,
1259 const JavaParamRef<jobject>& obj) {
1260 if (!base::FeatureList::IsEnabled(features::kAndroidAutofillAccessibility) ||
1261 g_element_after_element_hosting_autofill_popup_unique_id == -1 ||
1262 GetAXFromUniqueID(
1263 g_element_after_element_hosting_autofill_popup_unique_id) == nullptr)
1264 return 0;
1265
1266 return g_element_after_element_hosting_autofill_popup_unique_id;
1267 }
1268
IsAutofillPopupNode(JNIEnv * env,const JavaParamRef<jobject> & obj,jint unique_id)1269 jboolean WebContentsAccessibilityAndroid::IsAutofillPopupNode(
1270 JNIEnv* env,
1271 const JavaParamRef<jobject>& obj,
1272 jint unique_id) {
1273 auto* android_node =
1274 static_cast<BrowserAccessibilityAndroid*>(g_autofill_popup_proxy_node);
1275
1276 return g_autofill_popup_proxy_node && android_node->unique_id() == unique_id;
1277 }
1278
Scroll(JNIEnv * env,const JavaParamRef<jobject> & obj,jint unique_id,int direction,bool is_page_scroll)1279 bool WebContentsAccessibilityAndroid::Scroll(JNIEnv* env,
1280 const JavaParamRef<jobject>& obj,
1281 jint unique_id,
1282 int direction,
1283 bool is_page_scroll) {
1284 BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id);
1285 if (!node)
1286 return false;
1287
1288 return node->Scroll(direction, is_page_scroll);
1289 }
1290
SetRangeValue(JNIEnv * env,const JavaParamRef<jobject> & obj,jint unique_id,float value)1291 bool WebContentsAccessibilityAndroid::SetRangeValue(
1292 JNIEnv* env,
1293 const JavaParamRef<jobject>& obj,
1294 jint unique_id,
1295 float value) {
1296 BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id);
1297 if (!node)
1298 return false;
1299
1300 BrowserAccessibilityAndroid* android_node =
1301 static_cast<BrowserAccessibilityAndroid*>(node);
1302
1303 if (!android_node->GetData().IsRangeValueSupported())
1304 return false;
1305
1306 float min =
1307 node->GetFloatAttribute(ax::mojom::FloatAttribute::kMinValueForRange);
1308 float max =
1309 node->GetFloatAttribute(ax::mojom::FloatAttribute::kMaxValueForRange);
1310 if (max <= min)
1311 return false;
1312
1313 value = base::ClampToRange(value, min, max);
1314 node->manager()->SetValue(*node, base::NumberToString(value));
1315 return true;
1316 }
1317
AreInlineTextBoxesLoaded(JNIEnv * env,const JavaParamRef<jobject> & obj,jint unique_id)1318 jboolean WebContentsAccessibilityAndroid::AreInlineTextBoxesLoaded(
1319 JNIEnv* env,
1320 const JavaParamRef<jobject>& obj,
1321 jint unique_id) {
1322 BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id);
1323 if (!node)
1324 return false;
1325
1326 return node->AreInlineTextBoxesLoaded();
1327 }
1328
LoadInlineTextBoxes(JNIEnv * env,const JavaParamRef<jobject> & obj,jint unique_id)1329 void WebContentsAccessibilityAndroid::LoadInlineTextBoxes(
1330 JNIEnv* env,
1331 const JavaParamRef<jobject>& obj,
1332 jint unique_id) {
1333 BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id);
1334 if (node)
1335 node->manager()->LoadInlineTextBoxes(*node);
1336 }
1337
1338 base::android::ScopedJavaLocalRef<jintArray>
GetCharacterBoundingBoxes(JNIEnv * env,const base::android::JavaParamRef<jobject> & obj,jint unique_id,jint start,jint len)1339 WebContentsAccessibilityAndroid::GetCharacterBoundingBoxes(
1340 JNIEnv* env,
1341 const base::android::JavaParamRef<jobject>& obj,
1342 jint unique_id,
1343 jint start,
1344 jint len) {
1345 auto* root_manager = GetRootBrowserAccessibilityManager();
1346 if (!root_manager)
1347 return nullptr;
1348
1349 BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id);
1350 if (!node)
1351 return nullptr;
1352
1353 if (len <= 0 || len > kMaxCharacterBoundingBoxLen) {
1354 LOG(ERROR) << "Trying to request EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY "
1355 << "with a length of " << len << ". Valid values are between 1 "
1356 << "and " << kMaxCharacterBoundingBoxLen;
1357 return nullptr;
1358 }
1359
1360 float dip_scale =
1361 use_zoom_for_dsf_enabled_ ? 1 / root_manager->device_scale_factor() : 1.0;
1362
1363 gfx::Rect object_bounds = node->GetUnclippedRootFrameBoundsRect();
1364 int coords[4 * len];
1365 for (int i = 0; i < len; i++) {
1366 gfx::Rect char_bounds = node->GetUnclippedRootFrameInnerTextRangeBoundsRect(
1367 start + i, start + i + 1);
1368 if (char_bounds.IsEmpty())
1369 char_bounds = object_bounds;
1370
1371 char_bounds = gfx::ScaleToEnclosingRect(char_bounds, dip_scale, dip_scale);
1372
1373 coords[4 * i + 0] = char_bounds.x();
1374 coords[4 * i + 1] = char_bounds.y();
1375 coords[4 * i + 2] = char_bounds.right();
1376 coords[4 * i + 3] = char_bounds.bottom();
1377 }
1378 return base::android::ToJavaIntArray(env, coords,
1379 static_cast<size_t>(4 * len));
1380 }
1381
1382 BrowserAccessibilityManagerAndroid*
GetRootBrowserAccessibilityManager()1383 WebContentsAccessibilityAndroid::GetRootBrowserAccessibilityManager() {
1384 return static_cast<BrowserAccessibilityManagerAndroid*>(
1385 web_contents_->GetRootBrowserAccessibilityManager());
1386 }
1387
GetAXFromUniqueID(int32_t unique_id)1388 BrowserAccessibilityAndroid* WebContentsAccessibilityAndroid::GetAXFromUniqueID(
1389 int32_t unique_id) {
1390 return BrowserAccessibilityAndroid::GetFromUniqueId(unique_id);
1391 }
1392
UpdateFrameInfo(float page_scale)1393 void WebContentsAccessibilityAndroid::UpdateFrameInfo(float page_scale) {
1394 page_scale_ = page_scale;
1395 if (frame_info_initialized_)
1396 return;
1397
1398 JNIEnv* env = AttachCurrentThread();
1399 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
1400 if (obj.is_null())
1401 return;
1402
1403 Java_WebContentsAccessibilityImpl_notifyFrameInfoInitialized(env, obj);
1404 frame_info_initialized_ = true;
1405 }
1406
CollectStats()1407 void WebContentsAccessibilityAndroid::CollectStats() {
1408 JNIEnv* env = AttachCurrentThread();
1409 ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
1410 if (obj.is_null())
1411 return;
1412
1413 int event_type_mask =
1414 Java_WebContentsAccessibilityImpl_getAccessibilityServiceEventTypeMask(
1415 env, obj);
1416 EVENT_TYPE_HISTOGRAM(event_type_mask, ANNOUNCEMENT);
1417 EVENT_TYPE_HISTOGRAM(event_type_mask, ASSIST_READING_CONTEXT);
1418 EVENT_TYPE_HISTOGRAM(event_type_mask, GESTURE_DETECTION_END);
1419 EVENT_TYPE_HISTOGRAM(event_type_mask, GESTURE_DETECTION_START);
1420 EVENT_TYPE_HISTOGRAM(event_type_mask, NOTIFICATION_STATE_CHANGED);
1421 EVENT_TYPE_HISTOGRAM(event_type_mask, TOUCH_EXPLORATION_GESTURE_END);
1422 EVENT_TYPE_HISTOGRAM(event_type_mask, TOUCH_EXPLORATION_GESTURE_START);
1423 EVENT_TYPE_HISTOGRAM(event_type_mask, TOUCH_INTERACTION_END);
1424 EVENT_TYPE_HISTOGRAM(event_type_mask, TOUCH_INTERACTION_START);
1425 EVENT_TYPE_HISTOGRAM(event_type_mask, VIEW_ACCESSIBILITY_FOCUSED);
1426 EVENT_TYPE_HISTOGRAM(event_type_mask, VIEW_ACCESSIBILITY_FOCUS_CLEARED);
1427 EVENT_TYPE_HISTOGRAM(event_type_mask, VIEW_CLICKED);
1428 EVENT_TYPE_HISTOGRAM(event_type_mask, VIEW_CONTEXT_CLICKED);
1429 EVENT_TYPE_HISTOGRAM(event_type_mask, VIEW_FOCUSED);
1430 EVENT_TYPE_HISTOGRAM(event_type_mask, VIEW_HOVER_ENTER);
1431 EVENT_TYPE_HISTOGRAM(event_type_mask, VIEW_HOVER_EXIT);
1432 EVENT_TYPE_HISTOGRAM(event_type_mask, VIEW_LONG_CLICKED);
1433 EVENT_TYPE_HISTOGRAM(event_type_mask, VIEW_SCROLLED);
1434 EVENT_TYPE_HISTOGRAM(event_type_mask, VIEW_SELECTED);
1435 EVENT_TYPE_HISTOGRAM(event_type_mask, VIEW_TEXT_CHANGED);
1436 EVENT_TYPE_HISTOGRAM(event_type_mask, VIEW_TEXT_SELECTION_CHANGED);
1437 EVENT_TYPE_HISTOGRAM(event_type_mask,
1438 VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY);
1439 EVENT_TYPE_HISTOGRAM(event_type_mask, WINDOWS_CHANGED);
1440 EVENT_TYPE_HISTOGRAM(event_type_mask, WINDOW_CONTENT_CHANGED);
1441 EVENT_TYPE_HISTOGRAM(event_type_mask, WINDOW_STATE_CHANGED);
1442
1443 int feedback_type_mask =
1444 Java_WebContentsAccessibilityImpl_getAccessibilityServiceFeedbackTypeMask(
1445 env, obj);
1446 FEEDBACK_TYPE_HISTOGRAM(feedback_type_mask, SPOKEN);
1447 FEEDBACK_TYPE_HISTOGRAM(feedback_type_mask, HAPTIC);
1448 FEEDBACK_TYPE_HISTOGRAM(feedback_type_mask, AUDIBLE);
1449 FEEDBACK_TYPE_HISTOGRAM(feedback_type_mask, VISUAL);
1450 FEEDBACK_TYPE_HISTOGRAM(feedback_type_mask, GENERIC);
1451 FEEDBACK_TYPE_HISTOGRAM(feedback_type_mask, BRAILLE);
1452
1453 int flags_mask =
1454 Java_WebContentsAccessibilityImpl_getAccessibilityServiceFlagsMask(env,
1455 obj);
1456 FLAGS_HISTOGRAM(flags_mask, INCLUDE_NOT_IMPORTANT_VIEWS);
1457 FLAGS_HISTOGRAM(flags_mask, REQUEST_TOUCH_EXPLORATION_MODE);
1458 FLAGS_HISTOGRAM(flags_mask, REQUEST_ENHANCED_WEB_ACCESSIBILITY);
1459 FLAGS_HISTOGRAM(flags_mask, REPORT_VIEW_IDS);
1460 FLAGS_HISTOGRAM(flags_mask, REQUEST_FILTER_KEY_EVENTS);
1461 FLAGS_HISTOGRAM(flags_mask, RETRIEVE_INTERACTIVE_WINDOWS);
1462 FLAGS_HISTOGRAM(flags_mask, FORCE_DIRECT_BOOT_AWARE);
1463
1464 int capabilities_mask =
1465 Java_WebContentsAccessibilityImpl_getAccessibilityServiceCapabilitiesMask(
1466 env, obj);
1467 CAPABILITY_TYPE_HISTOGRAM(capabilities_mask, CAN_RETRIEVE_WINDOW_CONTENT);
1468 CAPABILITY_TYPE_HISTOGRAM(capabilities_mask, CAN_REQUEST_TOUCH_EXPLORATION);
1469 CAPABILITY_TYPE_HISTOGRAM(capabilities_mask,
1470 CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY);
1471 CAPABILITY_TYPE_HISTOGRAM(capabilities_mask, CAN_REQUEST_FILTER_KEY_EVENTS);
1472 CAPABILITY_TYPE_HISTOGRAM(capabilities_mask, CAN_CONTROL_MAGNIFICATION);
1473 CAPABILITY_TYPE_HISTOGRAM(capabilities_mask, CAN_PERFORM_GESTURES);
1474 }
1475
1476 base::WeakPtr<WebContentsAccessibilityAndroid>
GetWeakPtr()1477 WebContentsAccessibilityAndroid::GetWeakPtr() {
1478 return weak_ptr_factory_.GetWeakPtr();
1479 }
1480
JNI_WebContentsAccessibilityImpl_Init(JNIEnv * env,const JavaParamRef<jobject> & obj,const JavaParamRef<jobject> & jweb_contents)1481 jlong JNI_WebContentsAccessibilityImpl_Init(
1482 JNIEnv* env,
1483 const JavaParamRef<jobject>& obj,
1484 const JavaParamRef<jobject>& jweb_contents) {
1485 WebContents* web_contents = WebContents::FromJavaWebContents(jweb_contents);
1486 DCHECK(web_contents);
1487
1488 return reinterpret_cast<intptr_t>(
1489 new WebContentsAccessibilityAndroid(env, obj, web_contents));
1490 }
1491
1492 } // namespace content
1493