Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1841)

Unified Diff: content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java

Issue 15741009: Native Android accessibility. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
index eeebf036b9d11f23fecddf5bb13100e046585029..02d89d1dbf9a164d26a4847b4e28c3bcffceb198 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewCore.java
@@ -5,18 +5,23 @@
package org.chromium.content.browser;
import android.app.Activity;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
+import android.database.ContentObserver;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
+import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.ResultReceiver;
+import android.provider.Settings;
+import android.provider.Settings.Secure;
import android.text.Editable;
import android.util.Log;
import android.util.Pair;
@@ -30,7 +35,10 @@ import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
@@ -44,6 +52,7 @@ import org.chromium.base.WeakContext;
import org.chromium.content.R;
import org.chromium.content.browser.ContentViewGestureHandler.MotionEventDelegate;
import org.chromium.content.browser.accessibility.AccessibilityInjector;
+import org.chromium.content.browser.accessibility.BrowserAccessibilityManager;
import org.chromium.content.browser.input.AdapterInputConnection;
import org.chromium.content.browser.input.HandleView;
import org.chromium.content.browser.input.ImeAdapter;
@@ -58,6 +67,8 @@ import org.chromium.ui.WindowAndroid;
import org.chromium.ui.gfx.DeviceDisplayInfo;
import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -68,7 +79,9 @@ import java.util.Map;
* being tied to the view system.
*/
@JNINamespace("content")
-public class ContentViewCore implements MotionEventDelegate, NavigationClient {
+ public class ContentViewCore implements MotionEventDelegate,
+ NavigationClient,
+ AccessibilityStateChangeListener {
/**
* Indicates that input events are batched together and delivered just before vsync.
*/
@@ -348,6 +361,15 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
// The AccessibilityInjector that handles loading Accessibility scripts into the web page.
private AccessibilityInjector mAccessibilityInjector;
+ // Handles native accessibility, i.e. without any script injection.
+ private BrowserAccessibilityManager mBrowserAccessibilityManager;
+
+ // System accessibility service.
+ private final AccessibilityManager mAccessibilityManager;
+
+ // Allows us to dynamically respond when the accessibility script injection flag changes.
+ private ContentObserver mAccessibilityScriptInjectionObserver;
+
// Temporary notification to tell onSizeChanged to focus a form element,
// because the OSK was just brought up.
private boolean mUnfocusOnNextSizeChanged = false;
@@ -369,6 +391,7 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
private ViewAndroid mViewAndroid;
+
/**
* Constructs a new ContentViewCore. Embedders must call initialize() after constructing
* a ContentViewCore and before using it.
@@ -388,6 +411,8 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
mStartHandlePoint = mRenderCoordinates.createNormalizedPoint();
mEndHandlePoint = mRenderCoordinates.createNormalizedPoint();
mInsertionHandlePoint = mRenderCoordinates.createNormalizedPoint();
+ mAccessibilityManager = (AccessibilityManager)
+ getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
}
/**
@@ -634,7 +659,6 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
initializeContainerView(internalDispatcher, inputEventDeliveryMode);
mAccessibilityInjector = AccessibilityInjector.newInstance(this);
- mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
String contentDescription = "Web View";
if (R.string.accessibility_content_view == 0) {
@@ -773,6 +797,11 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
mContentSettings = null;
mJavaScriptInterfaces.clear();
mRetainedJavaScriptObjects.clear();
+ if (mAccessibilityScriptInjectionObserver != null) {
+ getContext().getContentResolver().unregisterContentObserver(
+ mAccessibilityScriptInjectionObserver);
+ mAccessibilityScriptInjectionObserver = null;
+ }
}
/**
@@ -1288,7 +1317,6 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
TraceEvent.begin();
hidePopupDialog();
nativeOnHide(mNativeContentViewCore);
- setAccessibilityState(false);
TraceEvent.end();
}
@@ -1297,7 +1325,7 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
*/
public void onActivityResume() {
nativeOnShow(mNativeContentViewCore);
- setAccessibilityState(true);
+ setAccessibilityState(mAccessibilityManager.isEnabled());
}
/**
@@ -1305,7 +1333,7 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
*/
public void onShow() {
nativeOnShow(mNativeContentViewCore);
- setAccessibilityState(true);
+ setAccessibilityState(mAccessibilityManager.isEnabled());
}
/**
@@ -1313,7 +1341,7 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
*/
public void onHide() {
hidePopupDialog();
- setAccessibilityState(false);
+ setInjectedAccessibility(false);
nativeOnHide(mNativeContentViewCore);
}
@@ -1366,7 +1394,7 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
ChildProcessLauncher.bindAsHighPriority(pid);
}
}
- setAccessibilityState(true);
+ setAccessibilityState(mAccessibilityManager.isEnabled());
}
/**
@@ -1381,7 +1409,7 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
ChildProcessLauncher.unbindAsHighPriority(pid);
}
}
- setAccessibilityState(false);
+ setInjectedAccessibility(false);
hidePopupDialog();
mZoomControlsDelegate.dismissZoomPicker();
}
@@ -1609,6 +1637,9 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
public boolean onHoverEvent(MotionEvent event) {
TraceEvent.begin("onHoverEvent");
mContainerView.removeCallbacks(mFakeMouseMoveRunnable);
+ if (mBrowserAccessibilityManager != null) {
+ return mBrowserAccessibilityManager.onHoverEvent(event);
+ }
if (mNativeContentViewCore != 0) {
nativeSendMouseMoveEvent(mNativeContentViewCore, event.getEventTime(),
event.getX(), event.getY());
@@ -2171,6 +2202,9 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
controlsOffsetPix, contentOffsetYPix, overdrawBottomHeightPix);
mPendingRendererFrame = true;
+ if (mBrowserAccessibilityManager != null) {
+ mBrowserAccessibilityManager.notifyFrameInfoInitialized();
+ }
}
@SuppressWarnings("unused")
@@ -2553,6 +2587,11 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
getContentViewClient().onStartContentIntent(getContext(), contentUrl);
}
+ @Override
+ public void onAccessibilityStateChanged(boolean enabled) {
+ setAccessibilityState(enabled);
+ }
+
/**
* Determines whether or not this ContentViewCore can handle this accessibility action.
* @param action The action to perform.
@@ -2581,9 +2620,44 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
}
/**
+ * Set the BrowserAccessibilityManager, used for native accessibility
+ * (not script injection). This is only set when system accessibility
+ * has been enabled.
+ * @param manager The new BrowserAccessibilityManager.
+ */
+ public void setBrowserAccessibilityManager(BrowserAccessibilityManager manager) {
+ mBrowserAccessibilityManager = manager;
+ }
+
+ /**
+ * Get the BrowserAccessibilityManager, used for native accessibility
+ * (not script injection). This will return null when system accessibility
+ * is not enabled.
+ * @return This view's BrowserAccessibilityManager.
+ */
+ public BrowserAccessibilityManager getBrowserAccessibilityManager() {
+ return mBrowserAccessibilityManager;
+ }
+
+ /**
+ * If native accessibility (not script injection) is enabled, and if this is
+ * running on JellyBean or later, returns an AccessibilityNodeProvider that
+ * implements native accessibility for this view. Returns null otherwise.
+ * @return The AccessibilityNodeProvider, if available, or null otherwise.
+ */
+ public AccessibilityNodeProvider getAccessibilityNodeProvider() {
+ if (mBrowserAccessibilityManager != null) {
+ return mBrowserAccessibilityManager.getAccessibilityNodeProvider();
+ } else {
+ return null;
+ }
+ }
+
+ /**
* @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
*/
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ // Note: this is only used by the script-injecting accessibility code.
mAccessibilityInjector.onInitializeAccessibilityNodeInfo(info);
}
@@ -2591,6 +2665,7 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
* @see View#onInitializeAccessibilityEvent(AccessibilityEvent)
*/
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ // Note: this is only used by the script-injecting accessibility code.
event.setClassName(this.getClass().getName());
// Identify where the top-left of the screen currently points to.
@@ -2612,6 +2687,46 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
}
/**
+ * Returns whether accessibility script injection is enabled on the device
+ */
+ public boolean isDeviceAccessibilityScriptInjectionEnabled() {
+ try {
+ if (!mContentSettings.getJavaScriptEnabled()) {
+ return false;
+ }
+
+ int result = getContext().checkCallingOrSelfPermission(
+ android.Manifest.permission.INTERNET);
+ if (result != PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+
+ Field field = Settings.Secure.class.getField("ACCESSIBILITY_SCRIPT_INJECTION");
+ field.setAccessible(true);
+ String accessibilityScriptInjection = (String) field.get(null);
+ ContentResolver contentResolver = getContext().getContentResolver();
+
+ if (mAccessibilityScriptInjectionObserver == null) {
+ ContentObserver contentObserver = new ContentObserver(new Handler()) {
+ public void onChange(boolean selfChange, Uri uri) {
+ setAccessibilityState(mAccessibilityManager.isEnabled());
+ }
+ };
+ contentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(accessibilityScriptInjection),
+ false,
+ contentObserver);
+ mAccessibilityScriptInjectionObserver = contentObserver;
+ }
+
+ return Settings.Secure.getInt(contentResolver, accessibilityScriptInjection, 0) == 1;
+ } catch (NoSuchFieldException e) {
+ } catch (IllegalAccessException e) {
+ }
+ return false;
+ }
+
+ /**
* Returns whether or not accessibility injection is being used.
*/
public boolean isInjectingAccessibilityScript() {
@@ -2619,10 +2734,40 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
}
/**
- * Enable or disable accessibility features.
+ * Turns browser accessibility on or off.
+ * If |state| is |false|, this turns off both native and injected accessibility.
+ * Otherwise, if accessibility script injection is enabled, this will enable the injected
+ * accessibility scripts, and if it is disabled this will enable the native accessibility.
*/
public void setAccessibilityState(boolean state) {
- mAccessibilityInjector.setScriptEnabled(state);
+ boolean injectedAccessibility = false;
+ boolean nativeAccessibility = false;
+ if (state) {
+ if (isDeviceAccessibilityScriptInjectionEnabled()) {
+ injectedAccessibility = true;
+ } else {
+ nativeAccessibility = true;
+ }
+ }
+ setInjectedAccessibility(injectedAccessibility);
+ setNativeAccessibilityState(nativeAccessibility);
+ }
+
+ /**
+ * Enable or disable native accessibility features.
+ */
+ public void setNativeAccessibilityState(boolean enabled) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ nativeSetAccessibilityEnabled(mNativeContentViewCore, enabled);
+ }
+ }
+
+ /**
+ * Enable or disable injected accessibility features
+ */
+ public void setInjectedAccessibility(boolean enabled) {
+ mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
+ mAccessibilityInjector.setScriptEnabled(enabled);
}
/**
@@ -2955,4 +3100,7 @@ public class ContentViewCore implements MotionEventDelegate, NavigationClient {
private native void nativeDetachExternalVideoSurface(
int nativeContentViewCoreImpl, int playerId);
+
+ private native void nativeSetAccessibilityEnabled(
+ int nativeContentViewCoreImpl, boolean enabled);
}
« no previous file with comments | « content/content_jni.gypi ('k') | content/public/android/java/src/org/chromium/content/browser/JellyBeanContentView.java » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698