Remove AccessibilityInjector support.
AccessibilityInjector is no longer used as it was needed for ICS support and Chrome only support API 16 and above now. BUG=479334 TBR=damienv@chromium.org Review URL: https://codereview.chromium.org/1100823004 Cr-Commit-Position: refs/heads/master@{#326419}
This commit is contained in:
android_webview
java
src
org
chromium
android_webview
test
shell
src
org
chromium
android_webview
chrome/android
java
src
org
chromium
chrome
javatests
src
org
chromium
chrome
browser
shell
java
src
org
chromium
chrome
chromecast/browser/android/apk/src/org/chromium/chromecast/shell
content
public
android
java
shell
android
java
src
org
chromium
content_shell
javatests
src
org
chromium
content_shell_apk
@ -194,11 +194,6 @@ public class AwContentViewClient extends ContentViewClient implements ContentVid
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isJavascriptEnabled() {
|
||||
return mAwSettings != null && mAwSettings.getJavaScriptEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExternalFlingActive() {
|
||||
return mAwContents.isFlingActive();
|
||||
|
@ -2245,14 +2245,14 @@ public class AwContents implements SmartClipProvider,
|
||||
* @see android.webkit.WebView#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
|
||||
*/
|
||||
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
|
||||
if (!isDestroyed()) mContentViewCore.onInitializeAccessibilityNodeInfo(info);
|
||||
// TODO(boliu): remove this method.
|
||||
}
|
||||
|
||||
/**
|
||||
* @see android.webkit.WebView#onInitializeAccessibilityEvent(AccessibilityEvent)
|
||||
*/
|
||||
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
|
||||
if (!isDestroyed()) mContentViewCore.onInitializeAccessibilityEvent(event);
|
||||
// TODO(boliu): remove this method.
|
||||
}
|
||||
|
||||
public boolean supportsAccessibilityAction(int action) {
|
||||
|
@ -15,8 +15,6 @@ import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.view.accessibility.AccessibilityNodeProvider;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
@ -411,20 +409,6 @@ public class AwTestContainerView extends FrameLayout {
|
||||
return provider == null ? super.getAccessibilityNodeProvider() : provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
|
||||
super.onInitializeAccessibilityNodeInfo(info);
|
||||
info.setClassName(AwContents.class.getName());
|
||||
mAwContents.onInitializeAccessibilityNodeInfo(info);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
|
||||
super.onInitializeAccessibilityEvent(event);
|
||||
event.setClassName(AwContents.class.getName());
|
||||
mAwContents.onInitializeAccessibilityEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performAccessibilityAction(int action, Bundle arguments) {
|
||||
return mAwContents.performAccessibilityAction(action, arguments);
|
||||
|
@ -1,20 +0,0 @@
|
||||
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package org.chromium.chrome.browser;
|
||||
|
||||
import org.chromium.chrome.browser.preferences.PrefServiceBridge;
|
||||
import org.chromium.content.browser.ContentViewClient;
|
||||
|
||||
/**
|
||||
* The default {@link ContentViewClient} implementation for the chrome layer embedders.
|
||||
*/
|
||||
public class ChromeContentViewClient extends ContentViewClient {
|
||||
|
||||
@Override
|
||||
public boolean isJavascriptEnabled() {
|
||||
return PrefServiceBridge.getInstance().javaScriptEnabled();
|
||||
}
|
||||
|
||||
}
|
@ -476,7 +476,7 @@ public class Tab implements ViewGroup.OnHierarchyChangeListener,
|
||||
* ContentViewClient that provides basic tab functionality and is meant to be extended
|
||||
* by child classes.
|
||||
*/
|
||||
protected class TabContentViewClient extends ChromeContentViewClient {
|
||||
protected class TabContentViewClient extends ContentViewClient {
|
||||
@Override
|
||||
public void onUpdateTitle(String title) {
|
||||
updateTitle(title);
|
||||
@ -1488,10 +1488,6 @@ public class Tab implements ViewGroup.OnHierarchyChangeListener,
|
||||
updateTitle();
|
||||
removeSadTabIfPresent();
|
||||
|
||||
if (getContentViewCore() != null) {
|
||||
getContentViewCore().stopCurrentAccessibilityNotifications();
|
||||
}
|
||||
|
||||
clearHungRendererState();
|
||||
|
||||
for (TabObserver observer : mObservers) observer.onPageLoadStarted(this);
|
||||
@ -1547,7 +1543,7 @@ public class Tab implements ViewGroup.OnHierarchyChangeListener,
|
||||
*/
|
||||
protected void initContentViewCore(WebContents webContents) {
|
||||
ContentViewCore cvc = new ContentViewCore(mContext);
|
||||
ContentView cv = ContentView.newInstance(mContext, cvc);
|
||||
ContentView cv = new ContentView(mContext, cvc);
|
||||
cv.setContentDescription(mContext.getResources().getString(
|
||||
R.string.accessibility_content_view));
|
||||
cvc.initialize(cv, cv, webContents, getWindowAndroid());
|
||||
@ -2165,7 +2161,7 @@ public class Tab implements ViewGroup.OnHierarchyChangeListener,
|
||||
private void swapWebContents(
|
||||
WebContents webContents, boolean didStartLoad, boolean didFinishLoad) {
|
||||
ContentViewCore cvc = new ContentViewCore(mContext);
|
||||
ContentView cv = ContentView.newInstance(mContext, cvc);
|
||||
ContentView cv = new ContentView(mContext, cvc);
|
||||
cv.setContentDescription(mContext.getResources().getString(
|
||||
R.string.accessibility_content_view));
|
||||
cvc.initialize(cv, cv, webContents, getWindowAndroid());
|
||||
|
@ -24,11 +24,11 @@ import org.chromium.base.ApplicationStatus;
|
||||
import org.chromium.base.ApplicationStatus.ActivityStateListener;
|
||||
import org.chromium.base.CalledByNative;
|
||||
import org.chromium.base.FieldTrialList;
|
||||
import org.chromium.chrome.browser.ChromeContentViewClient;
|
||||
import org.chromium.chrome.browser.ContentViewUtil;
|
||||
import org.chromium.chrome.browser.EmptyTabObserver;
|
||||
import org.chromium.chrome.browser.Tab;
|
||||
import org.chromium.content.browser.ContentView;
|
||||
import org.chromium.content.browser.ContentViewClient;
|
||||
import org.chromium.content.browser.ContentViewCore;
|
||||
import org.chromium.content_public.browser.JavaScriptCallback;
|
||||
import org.chromium.content_public.browser.LoadUrlParams;
|
||||
@ -600,7 +600,7 @@ public class TransitionPageHelper extends EmptyTabObserver {
|
||||
if (mNativeTransitionPageHelperPtr == 0) return;
|
||||
|
||||
mTransitionContentViewCore = new ContentViewCore(mContext);
|
||||
ContentView cv = ContentView.newInstance(mContext, mTransitionContentViewCore);
|
||||
ContentView cv = new ContentView(mContext, mTransitionContentViewCore);
|
||||
mTransitionContentViewCore.initialize(cv, cv,
|
||||
ContentViewUtil.createWebContentsWithSharedSiteInstance(mSourceContentViewCore),
|
||||
mWindowAndroid);
|
||||
@ -617,7 +617,7 @@ public class TransitionPageHelper extends EmptyTabObserver {
|
||||
nativeSetWebContents(mNativeTransitionPageHelperPtr, mTransitionContentViewCore);
|
||||
setTransitionOpacity(0.0f);
|
||||
|
||||
mTransitionContentViewCore.setContentViewClient(new ChromeContentViewClient() {
|
||||
mTransitionContentViewCore.setContentViewClient(new ContentViewClient() {
|
||||
@Override
|
||||
public void onOffsetsForFullscreenChanged(
|
||||
float topControlsOffsetYPix,
|
||||
|
2
chrome/android/javatests/src/org/chromium/chrome/browser/input/SelectPopupOtherContentViewTest.java
2
chrome/android/javatests/src/org/chromium/chrome/browser/input/SelectPopupOtherContentViewTest.java
@ -77,7 +77,7 @@ public class SelectPopupOtherContentViewTest extends ChromeShellTestBase {
|
||||
WindowAndroid windowAndroid = new ActivityWindowAndroid(getActivity());
|
||||
|
||||
ContentViewCore contentViewCore = new ContentViewCore(getActivity());
|
||||
ContentView cv = ContentView.newInstance(getActivity(), contentViewCore);
|
||||
ContentView cv = new ContentView(getActivity(), contentViewCore);
|
||||
contentViewCore.initialize(cv, cv, webContents, windowAndroid);
|
||||
contentViewCore.destroy();
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ package org.chromium.chrome.shell;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.chromium.chrome.browser.ChromeContentViewClient;
|
||||
import org.chromium.chrome.browser.Tab;
|
||||
import org.chromium.chrome.browser.tabmodel.EmptyTabModel;
|
||||
import org.chromium.chrome.browser.tabmodel.TabModel;
|
||||
@ -86,7 +85,7 @@ class ChromeShellTabModelSelector extends TabModelSelectorBase {
|
||||
public Tab openNewTab(LoadUrlParams loadUrlParams, TabLaunchType type, Tab parent,
|
||||
boolean incognito) {
|
||||
assert !incognito;
|
||||
ContentViewClient client = new ChromeContentViewClient() {
|
||||
ContentViewClient client = new ContentViewClient() {
|
||||
@Override
|
||||
public ContentVideoViewClient getContentVideoViewClient() {
|
||||
return mContentVideoViewClient;
|
||||
|
@ -127,7 +127,7 @@ public class CastWindowAndroid extends LinearLayout {
|
||||
private void initFromNativeWebContents(WebContents webContents, int renderProcessId) {
|
||||
Context context = getContext();
|
||||
mContentViewCore = new ContentViewCore(context);
|
||||
ContentView view = ContentView.newInstance(context, mContentViewCore);
|
||||
ContentView view = new ContentView(context, mContentViewCore);
|
||||
mContentViewCore.initialize(view, view, webContents, mWindow);
|
||||
mWebContents = mContentViewCore.getWebContents();
|
||||
mNavigationController = mWebContents.getNavigationController();
|
||||
|
@ -8,7 +8,6 @@ import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
@ -16,8 +15,7 @@ import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.view.accessibility.AccessibilityNodeProvider;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import android.widget.FrameLayout;
|
||||
@ -40,17 +38,8 @@ public class ContentView extends FrameLayout
|
||||
* @param context The Context the view is running in, through which it can
|
||||
* access the current theme, resources, etc.
|
||||
* @param cvc A pointer to the content view core managing this content view.
|
||||
* @return A ContentView instance.
|
||||
*/
|
||||
public static ContentView newInstance(Context context, ContentViewCore cvc) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
|
||||
return new ContentView(context, cvc);
|
||||
} else {
|
||||
return new JellyBeanContentView(context, cvc);
|
||||
}
|
||||
}
|
||||
|
||||
protected ContentView(Context context, ContentViewCore cvc) {
|
||||
public ContentView(Context context, ContentViewCore cvc) {
|
||||
super(context, null, android.R.attr.webViewStyle);
|
||||
|
||||
if (getScrollBarStyle() == View.SCROLLBARS_INSIDE_OVERLAY) {
|
||||
@ -64,6 +53,25 @@ public class ContentView extends FrameLayout
|
||||
mContentViewCore = cvc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performAccessibilityAction(int action, Bundle arguments) {
|
||||
if (mContentViewCore.supportsAccessibilityAction(action)) {
|
||||
return mContentViewCore.performAccessibilityAction(action, arguments);
|
||||
}
|
||||
|
||||
return super.performAccessibilityAction(action, arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessibilityNodeProvider getAccessibilityNodeProvider() {
|
||||
AccessibilityNodeProvider provider = mContentViewCore.getAccessibilityNodeProvider();
|
||||
if (provider != null) {
|
||||
return provider;
|
||||
} else {
|
||||
return super.getAccessibilityNodeProvider();
|
||||
}
|
||||
}
|
||||
|
||||
// Needed by ContentViewCore.InternalAccessDelegate
|
||||
@Override
|
||||
public boolean drawChild(Canvas canvas, View child, long drawingTime) {
|
||||
@ -225,22 +233,6 @@ public class ContentView extends FrameLayout
|
||||
return super.awakenScrollBars();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
|
||||
super.onInitializeAccessibilityNodeInfo(info);
|
||||
mContentViewCore.onInitializeAccessibilityNodeInfo(info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills in scrolling values for AccessibilityEvents.
|
||||
* @param event Event being fired.
|
||||
*/
|
||||
@Override
|
||||
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
|
||||
super.onInitializeAccessibilityEvent(event);
|
||||
mContentViewCore.onInitializeAccessibilityEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
@ -273,6 +265,7 @@ public class ContentView extends FrameLayout
|
||||
return;
|
||||
}
|
||||
mContentViewCore.setSmartClipDataListener(new ContentViewCore.SmartClipDataListener() {
|
||||
@Override
|
||||
public void onSmartClipDataExtracted(String text, String html, Rect clipRect) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("url", mContentViewCore.getWebContents().getVisibleUrl());
|
||||
|
@ -170,15 +170,6 @@ public class ContentViewClient {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether javascript is enabled by the embedder.
|
||||
*/
|
||||
// TODO(tedchoc): Only used for ICS accessibility injection, so remove this method when
|
||||
// that is no longer needed.
|
||||
public boolean isJavascriptEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether an externally managed (i.e., not compositor-driven) fling
|
||||
* of this ContentView is active.
|
||||
|
@ -9,7 +9,6 @@ import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.app.SearchManager;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.Intent;
|
||||
@ -19,14 +18,12 @@ import android.database.ContentObserver;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
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.os.SystemClock;
|
||||
import android.provider.Browser;
|
||||
import android.provider.Settings;
|
||||
import android.text.Editable;
|
||||
import android.text.Selection;
|
||||
import android.text.TextUtils;
|
||||
@ -38,10 +35,8 @@ import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
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;
|
||||
@ -58,7 +53,6 @@ import org.chromium.base.TraceEvent;
|
||||
import org.chromium.base.VisibleForTesting;
|
||||
import org.chromium.content.R;
|
||||
import org.chromium.content.browser.ScreenOrientationListener.ScreenOrientationObserver;
|
||||
import org.chromium.content.browser.accessibility.AccessibilityInjector;
|
||||
import org.chromium.content.browser.accessibility.BrowserAccessibilityManager;
|
||||
import org.chromium.content.browser.accessibility.captioning.CaptioningBridgeFactory;
|
||||
import org.chromium.content.browser.accessibility.captioning.SystemCaptioningBridge;
|
||||
@ -89,7 +83,6 @@ import org.chromium.ui.touch_selection.SelectionEventType;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@ -313,20 +306,6 @@ public class ContentViewCore
|
||||
mWeakContentViewCore = new WeakReference<ContentViewCore>(contentViewCore);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didStartLoading(String url) {
|
||||
ContentViewCore contentViewCore = mWeakContentViewCore.get();
|
||||
if (contentViewCore == null) return;
|
||||
contentViewCore.mAccessibilityInjector.onPageLoadStarted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didStopLoading(String url) {
|
||||
ContentViewCore contentViewCore = mWeakContentViewCore.get();
|
||||
if (contentViewCore == null) return;
|
||||
contentViewCore.mAccessibilityInjector.onPageLoadStopped();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didFailLoad(boolean isProvisionalLoad, boolean isMainFrame, int errorCode,
|
||||
String description, String failingUrl) {
|
||||
@ -539,9 +518,6 @@ public class ContentViewCore
|
||||
// Delegate that will handle GET downloads, and be notified of completion of POST downloads.
|
||||
private ContentViewDownloadDelegate mDownloadDelegate;
|
||||
|
||||
// The AccessibilityInjector that handles loading Accessibility scripts into the web page.
|
||||
private AccessibilityInjector mAccessibilityInjector;
|
||||
|
||||
// Whether native accessibility, i.e. without any script injection, is allowed.
|
||||
private boolean mNativeAccessibilityAllowed;
|
||||
|
||||
@ -837,8 +813,6 @@ public class ContentViewCore
|
||||
mImeAdapter = createImeAdapter();
|
||||
attachImeAdapter();
|
||||
|
||||
mAccessibilityInjector = AccessibilityInjector.newInstance(this);
|
||||
|
||||
mWebContentsObserver = new ContentViewWebContentsObserver(this);
|
||||
}
|
||||
|
||||
@ -1442,7 +1416,6 @@ public class ContentViewCore
|
||||
public void onHide() {
|
||||
assert mWebContents != null;
|
||||
hidePopupsAndPreserveSelection();
|
||||
setInjectedAccessibility(false);
|
||||
mWebContents.onHide();
|
||||
}
|
||||
|
||||
@ -1514,7 +1487,6 @@ public class ContentViewCore
|
||||
@SuppressWarnings("javadoc")
|
||||
@SuppressLint("MissingSuperCall")
|
||||
public void onDetachedFromWindow() {
|
||||
setInjectedAccessibility(false);
|
||||
mZoomControlsDelegate.dismissZoomPicker();
|
||||
unregisterAccessibilityContentObserver();
|
||||
|
||||
@ -2878,7 +2850,8 @@ public class ContentViewCore
|
||||
* @return Whether or not this action is supported.
|
||||
*/
|
||||
public boolean supportsAccessibilityAction(int action) {
|
||||
return mAccessibilityInjector.supportsAccessibilityAction(action);
|
||||
// TODO(dmazzoni): implement this in BrowserAccessibilityManager.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2892,10 +2865,7 @@ public class ContentViewCore
|
||||
* the super {@link View} class.
|
||||
*/
|
||||
public boolean performAccessibilityAction(int action, Bundle arguments) {
|
||||
if (mAccessibilityInjector.supportsAccessibilityAction(action)) {
|
||||
return mAccessibilityInjector.performAccessibilityAction(action, arguments);
|
||||
}
|
||||
|
||||
// TODO(dmazzoni): implement this in BrowserAccessibilityManager.
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2955,96 +2925,6 @@ public class ContentViewCore
|
||||
settings.getTextTrackTextShadow(), settings.getTextTrackTextSize());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
|
||||
*/
|
||||
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
|
||||
// Note: this is only used by the script-injecting accessibility code.
|
||||
mAccessibilityInjector.onInitializeAccessibilityNodeInfo(info);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see View#onInitializeAccessibilityEvent(AccessibilityEvent)
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
|
||||
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.
|
||||
event.setScrollX(mRenderCoordinates.getScrollXPixInt());
|
||||
event.setScrollY(mRenderCoordinates.getScrollYPixInt());
|
||||
|
||||
// The maximum scroll values are determined by taking the content dimensions and
|
||||
// subtracting off the actual dimensions of the ChromeView.
|
||||
int maxScrollXPix = Math.max(0, mRenderCoordinates.getMaxHorizontalScrollPixInt());
|
||||
int maxScrollYPix = Math.max(0, mRenderCoordinates.getMaxVerticalScrollPixInt());
|
||||
event.setScrollable(maxScrollXPix > 0 || maxScrollYPix > 0);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
|
||||
event.setMaxScrollX(maxScrollXPix);
|
||||
event.setMaxScrollY(maxScrollYPix);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether accessibility script injection is enabled on the device
|
||||
*/
|
||||
public boolean isDeviceAccessibilityScriptInjectionEnabled() {
|
||||
try {
|
||||
// On JellyBean and higher, native accessibility is the default so script
|
||||
// injection is only allowed if enabled via a flag.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
|
||||
&& !CommandLine.getInstance().hasSwitch(
|
||||
ContentSwitches.ENABLE_ACCESSIBILITY_SCRIPT_INJECTION)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mContentViewClient.isJavascriptEnabled()) {
|
||||
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()) {
|
||||
@Override
|
||||
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) {
|
||||
// Do nothing, default to false.
|
||||
} catch (IllegalAccessException e) {
|
||||
// Do nothing, default to false.
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not accessibility injection is being used.
|
||||
*/
|
||||
public boolean isInjectingAccessibilityScript() {
|
||||
return mAccessibilityInjector.accessibilityIsAvailable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if accessibility is on and touch exploration is enabled.
|
||||
*/
|
||||
@ -3060,32 +2940,14 @@ public class ContentViewCore
|
||||
*/
|
||||
public void setAccessibilityState(boolean state) {
|
||||
if (!state) {
|
||||
setInjectedAccessibility(false);
|
||||
mNativeAccessibilityAllowed = false;
|
||||
mTouchExplorationEnabled = false;
|
||||
} else {
|
||||
boolean useScriptInjection = isDeviceAccessibilityScriptInjectionEnabled();
|
||||
setInjectedAccessibility(useScriptInjection);
|
||||
mNativeAccessibilityAllowed = !useScriptInjection;
|
||||
mNativeAccessibilityAllowed = true;
|
||||
mTouchExplorationEnabled = mAccessibilityManager.isTouchExplorationEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable injected accessibility features
|
||||
*/
|
||||
public void setInjectedAccessibility(boolean enabled) {
|
||||
mAccessibilityInjector.addOrRemoveAccessibilityApisIfNecessary();
|
||||
mAccessibilityInjector.setScriptEnabled(enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop any TTS notifications that are currently going on.
|
||||
*/
|
||||
public void stopCurrentAccessibilityNotifications() {
|
||||
mAccessibilityInjector.onPageLostFocus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether or not we should set accessibility focus on page load.
|
||||
*/
|
||||
|
@ -1,37 +0,0 @@
|
||||
// Copyright 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package org.chromium.content.browser;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.accessibility.AccessibilityNodeProvider;
|
||||
|
||||
/**
|
||||
* A version of {@link ContentView} that supports JellyBean features.
|
||||
*/
|
||||
class JellyBeanContentView extends ContentView {
|
||||
JellyBeanContentView(Context context, ContentViewCore cvc) {
|
||||
super(context, cvc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performAccessibilityAction(int action, Bundle arguments) {
|
||||
if (mContentViewCore.supportsAccessibilityAction(action)) {
|
||||
return mContentViewCore.performAccessibilityAction(action, arguments);
|
||||
}
|
||||
|
||||
return super.performAccessibilityAction(action, arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessibilityNodeProvider getAccessibilityNodeProvider() {
|
||||
AccessibilityNodeProvider provider = mContentViewCore.getAccessibilityNodeProvider();
|
||||
if (provider != null) {
|
||||
return provider;
|
||||
} else {
|
||||
return super.getAccessibilityNodeProvider();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,457 +0,0 @@
|
||||
// Copyright 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package org.chromium.content.browser.accessibility;
|
||||
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Vibrator;
|
||||
import android.speech.tts.TextToSpeech;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.utils.URLEncodedUtils;
|
||||
import org.chromium.base.CommandLine;
|
||||
import org.chromium.content.browser.ContentViewCore;
|
||||
import org.chromium.content.browser.JavascriptInterface;
|
||||
import org.chromium.content.common.ContentSwitches;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Responsible for accessibility injection and management of a {@link ContentViewCore}.
|
||||
*/
|
||||
public class AccessibilityInjector {
|
||||
// The ContentView this injector is responsible for managing.
|
||||
protected ContentViewCore mContentViewCore;
|
||||
|
||||
// The Java objects that are exposed to JavaScript
|
||||
private TextToSpeechWrapper mTextToSpeech;
|
||||
private VibratorWrapper mVibrator;
|
||||
private final boolean mHasVibratePermission;
|
||||
|
||||
// Lazily loaded helper objects.
|
||||
private AccessibilityManager mAccessibilityManager;
|
||||
|
||||
// Whether or not we should be injecting the script.
|
||||
protected boolean mInjectedScriptEnabled;
|
||||
protected boolean mScriptInjected;
|
||||
|
||||
private final String mAccessibilityScreenReaderUrl;
|
||||
|
||||
// To support building against the JELLY_BEAN and not JELLY_BEAN_MR1 SDK we need to add this
|
||||
// constant here.
|
||||
private static final int FEEDBACK_BRAILLE = 0x00000020;
|
||||
|
||||
// constants for determining script injection strategy
|
||||
private static final int ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED = -1;
|
||||
private static final int ACCESSIBILITY_SCRIPT_INJECTION_OPTED_OUT = 0;
|
||||
private static final int ACCESSIBILITY_SCRIPT_INJECTION_PROVIDED = 1;
|
||||
private static final String ALIAS_ACCESSIBILITY_JS_INTERFACE = "accessibility";
|
||||
private static final String ALIAS_ACCESSIBILITY_JS_INTERFACE_2 = "accessibility2";
|
||||
|
||||
// Template for JavaScript that injects a screen-reader.
|
||||
private static final String DEFAULT_ACCESSIBILITY_SCREEN_READER_URL =
|
||||
"https://ssl.gstatic.com/accessibility/javascript/android/chromeandroidvox.js";
|
||||
|
||||
private static final String ACCESSIBILITY_SCREEN_READER_JAVASCRIPT_TEMPLATE =
|
||||
"(function() {"
|
||||
+ " var chooser = document.createElement('script');"
|
||||
+ " chooser.type = 'text/javascript';"
|
||||
+ " chooser.src = '%1s';"
|
||||
+ " document.getElementsByTagName('head')[0].appendChild(chooser);"
|
||||
+ " })();";
|
||||
|
||||
// JavaScript call to turn ChromeVox on or off.
|
||||
private static final String TOGGLE_CHROME_VOX_JAVASCRIPT =
|
||||
"(function() {"
|
||||
+ " if (typeof cvox !== 'undefined') {"
|
||||
+ " cvox.ChromeVox.host.activateOrDeactivateChromeVox(%1s);"
|
||||
+ " }"
|
||||
+ " })();";
|
||||
|
||||
/**
|
||||
* Returns an instance of the {@link AccessibilityInjector} based on the SDK version.
|
||||
* @param view The ContentViewCore that this AccessibilityInjector manages.
|
||||
* @return An instance of a {@link AccessibilityInjector}.
|
||||
*/
|
||||
public static AccessibilityInjector newInstance(ContentViewCore view) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
return new LollipopAccessibilityInjector(view);
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||
return new JellyBeanAccessibilityInjector(view);
|
||||
} else {
|
||||
return new AccessibilityInjector(view);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of the IceCreamSandwichAccessibilityInjector.
|
||||
* @param view The ContentViewCore that this AccessibilityInjector manages.
|
||||
*/
|
||||
protected AccessibilityInjector(ContentViewCore view) {
|
||||
mContentViewCore = view;
|
||||
|
||||
mAccessibilityScreenReaderUrl = CommandLine.getInstance().getSwitchValue(
|
||||
ContentSwitches.ACCESSIBILITY_JAVASCRIPT_URL,
|
||||
DEFAULT_ACCESSIBILITY_SCREEN_READER_URL);
|
||||
|
||||
mHasVibratePermission = mContentViewCore.getContext().checkCallingOrSelfPermission(
|
||||
android.Manifest.permission.VIBRATE) == PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects a <script> tag into the current web site that pulls in the ChromeVox script for
|
||||
* accessibility support. Only injects if accessibility is turned on by
|
||||
* {@link AccessibilityManager#isEnabled()}, accessibility script injection is turned on, and
|
||||
* javascript is enabled on this page.
|
||||
*
|
||||
* @see AccessibilityManager#isEnabled()
|
||||
*/
|
||||
public void injectAccessibilityScriptIntoPage() {
|
||||
if (!accessibilityIsAvailable()) return;
|
||||
|
||||
int axsParameterValue = getAxsUrlParameterValue();
|
||||
if (axsParameterValue != ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED) {
|
||||
return;
|
||||
}
|
||||
|
||||
String js = getScreenReaderInjectingJs();
|
||||
if (mContentViewCore.isDeviceAccessibilityScriptInjectionEnabled()
|
||||
&& js != null && mContentViewCore.isAlive()) {
|
||||
addOrRemoveAccessibilityApisIfNecessary();
|
||||
mContentViewCore.getWebContents().evaluateJavaScript(js, null);
|
||||
mInjectedScriptEnabled = true;
|
||||
mScriptInjected = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles adding or removing accessibility related Java objects ({@link TextToSpeech} and
|
||||
* {@link Vibrator}) interfaces from Javascript. This method should be called at a time when it
|
||||
* is safe to add or remove these interfaces, specifically when the {@link ContentViewCore} is
|
||||
* first initialized or right before the {@link ContentViewCore} is about to navigate to a URL
|
||||
* or reload.
|
||||
* <p>
|
||||
* If this method is called at other times, the interfaces might not be correctly removed,
|
||||
* meaning that Javascript can still access these Java objects that may have been already
|
||||
* shut down.
|
||||
*/
|
||||
public void addOrRemoveAccessibilityApisIfNecessary() {
|
||||
if (accessibilityIsAvailable()) {
|
||||
addAccessibilityApis();
|
||||
} else {
|
||||
removeAccessibilityApis();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not touch to explore is enabled on the system.
|
||||
*/
|
||||
public boolean accessibilityIsAvailable() {
|
||||
if (!getAccessibilityManager().isEnabled()
|
||||
|| !mContentViewCore.getContentViewClient().isJavascriptEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// Check that there is actually a service running that requires injecting this script.
|
||||
List<AccessibilityServiceInfo> services =
|
||||
getAccessibilityManager().getEnabledAccessibilityServiceList(
|
||||
FEEDBACK_BRAILLE | AccessibilityServiceInfo.FEEDBACK_SPOKEN);
|
||||
return services.size() > 0;
|
||||
} catch (NullPointerException e) {
|
||||
// getEnabledAccessibilityServiceList() can throw an NPE due to a bad
|
||||
// AccessibilityService.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether or not the script is enabled. If the script is disabled, we also stop any
|
||||
* we output that is occurring. If the script has not yet been injected, injects it.
|
||||
* @param enabled Whether or not to enable the script.
|
||||
*/
|
||||
public void setScriptEnabled(boolean enabled) {
|
||||
if (enabled && !mScriptInjected) injectAccessibilityScriptIntoPage();
|
||||
if (!accessibilityIsAvailable() || mInjectedScriptEnabled == enabled) return;
|
||||
|
||||
mInjectedScriptEnabled = enabled;
|
||||
if (mContentViewCore.isAlive()) {
|
||||
String js = String.format(TOGGLE_CHROME_VOX_JAVASCRIPT, Boolean.toString(
|
||||
mInjectedScriptEnabled));
|
||||
mContentViewCore.getWebContents().evaluateJavaScript(js, null);
|
||||
|
||||
if (!mInjectedScriptEnabled) {
|
||||
// Stop any TTS/Vibration right now.
|
||||
onPageLostFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies this handler that a page load has started, which means we should mark the
|
||||
* accessibility script as not being injected. This way we can properly ignore incoming
|
||||
* accessibility gesture events.
|
||||
*/
|
||||
public void onPageLoadStarted() {
|
||||
mScriptInjected = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies this handler that a page load has stopped, which means we can now inject the
|
||||
* accessibility script.
|
||||
*/
|
||||
public void onPageLoadStopped() {
|
||||
injectAccessibilityScriptIntoPage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop any notifications that are currently going on (e.g. Text-to-Speech).
|
||||
*/
|
||||
public void onPageLostFocus() {
|
||||
if (mContentViewCore.isAlive()) {
|
||||
if (mTextToSpeech != null) mTextToSpeech.stop();
|
||||
if (mVibrator != null) mVibrator.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes an {@link AccessibilityNodeInfo} with the actions and movement granularity
|
||||
* levels supported by this {@link AccessibilityInjector}.
|
||||
* <p>
|
||||
* If an action identifier is added in this method, this {@link AccessibilityInjector} should
|
||||
* also return {@code true} from {@link #supportsAccessibilityAction(int)}.
|
||||
* </p>
|
||||
*
|
||||
* @param info The info to initialize.
|
||||
* @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
|
||||
*/
|
||||
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { }
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this {@link AccessibilityInjector} should handle the specified
|
||||
* action.
|
||||
*
|
||||
* @param action An accessibility action identifier.
|
||||
* @return {@code true} if this {@link AccessibilityInjector} should handle the specified
|
||||
* action.
|
||||
*/
|
||||
public boolean supportsAccessibilityAction(int action) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the specified accessibility action.
|
||||
*
|
||||
* @param action The identifier of the action to perform.
|
||||
* @param arguments The action arguments, or {@code null} if no arguments.
|
||||
* @return {@code true} if the action was successful.
|
||||
* @see View#performAccessibilityAction(int, Bundle)
|
||||
*/
|
||||
public boolean performAccessibilityAction(int action, Bundle arguments) {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void addAccessibilityApis() {
|
||||
Context context = mContentViewCore.getContext();
|
||||
if (context != null) {
|
||||
// Enabled, we should try to add if we have to.
|
||||
if (mTextToSpeech == null) {
|
||||
mTextToSpeech = createTextToSpeechWrapper(mContentViewCore.getContainerView(),
|
||||
context);
|
||||
mContentViewCore.addJavascriptInterface(mTextToSpeech,
|
||||
ALIAS_ACCESSIBILITY_JS_INTERFACE);
|
||||
}
|
||||
|
||||
if (mVibrator == null && mHasVibratePermission) {
|
||||
mVibrator = new VibratorWrapper(context);
|
||||
mContentViewCore.addJavascriptInterface(mVibrator,
|
||||
ALIAS_ACCESSIBILITY_JS_INTERFACE_2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void removeAccessibilityApis() {
|
||||
if (mTextToSpeech != null) {
|
||||
mContentViewCore.removeJavascriptInterface(ALIAS_ACCESSIBILITY_JS_INTERFACE);
|
||||
mTextToSpeech.stop();
|
||||
mTextToSpeech.shutdownInternal();
|
||||
mTextToSpeech = null;
|
||||
}
|
||||
|
||||
if (mVibrator != null) {
|
||||
mContentViewCore.removeJavascriptInterface(ALIAS_ACCESSIBILITY_JS_INTERFACE_2);
|
||||
mVibrator.cancel();
|
||||
mVibrator = null;
|
||||
}
|
||||
}
|
||||
|
||||
private int getAxsUrlParameterValue() {
|
||||
if (mContentViewCore.getWebContents().getUrl() == null) {
|
||||
return ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED;
|
||||
}
|
||||
|
||||
try {
|
||||
List<NameValuePair> params = URLEncodedUtils.parse(
|
||||
new URI(mContentViewCore.getWebContents().getUrl()), null);
|
||||
|
||||
for (NameValuePair param : params) {
|
||||
if ("axs".equals(param.getName())) {
|
||||
return Integer.parseInt(param.getValue());
|
||||
}
|
||||
}
|
||||
} catch (URISyntaxException ex) {
|
||||
// Intentional no-op.
|
||||
} catch (NumberFormatException ex) {
|
||||
// Intentional no-op.
|
||||
} catch (IllegalArgumentException ex) {
|
||||
// Intentional no-op.
|
||||
}
|
||||
|
||||
return ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED;
|
||||
}
|
||||
|
||||
private String getScreenReaderInjectingJs() {
|
||||
return String.format(ACCESSIBILITY_SCREEN_READER_JAVASCRIPT_TEMPLATE,
|
||||
mAccessibilityScreenReaderUrl);
|
||||
}
|
||||
|
||||
private AccessibilityManager getAccessibilityManager() {
|
||||
if (mAccessibilityManager == null) {
|
||||
mAccessibilityManager = (AccessibilityManager) mContentViewCore.getContext()
|
||||
.getSystemService(Context.ACCESSIBILITY_SERVICE);
|
||||
}
|
||||
|
||||
return mAccessibilityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to protect how long JavaScript can vibrate for. This isn't a good comprehensive
|
||||
* protection, just used to cover mistakes and protect against long vibrate durations/repeats.
|
||||
*
|
||||
* Also only exposes methods we *want* to expose, no others for the class.
|
||||
*/
|
||||
private static class VibratorWrapper {
|
||||
private static final long MAX_VIBRATE_DURATION_MS = 5000;
|
||||
|
||||
private final Vibrator mVibrator;
|
||||
|
||||
public VibratorWrapper(Context context) {
|
||||
mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
@SuppressWarnings("unused")
|
||||
public boolean hasVibrator() {
|
||||
return mVibrator.hasVibrator();
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
@SuppressWarnings("unused")
|
||||
public void vibrate(long milliseconds) {
|
||||
milliseconds = Math.min(milliseconds, MAX_VIBRATE_DURATION_MS);
|
||||
mVibrator.vibrate(milliseconds);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
@SuppressWarnings("unused")
|
||||
public void vibrate(long[] pattern, int repeat) {
|
||||
for (int i = 0; i < pattern.length; ++i) {
|
||||
pattern[i] = Math.min(pattern[i], MAX_VIBRATE_DURATION_MS);
|
||||
}
|
||||
|
||||
repeat = -1;
|
||||
|
||||
mVibrator.vibrate(pattern, repeat);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
@SuppressWarnings("unused")
|
||||
public void cancel() {
|
||||
mVibrator.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
protected TextToSpeechWrapper createTextToSpeechWrapper(View view, Context context) {
|
||||
return new TextToSpeechWrapper(view, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to protect the TextToSpeech class, only exposing the methods we want to expose.
|
||||
*/
|
||||
protected static class TextToSpeechWrapper {
|
||||
protected final TextToSpeech mTextToSpeech;
|
||||
private final View mView;
|
||||
|
||||
protected TextToSpeechWrapper(View view, Context context) {
|
||||
mView = view;
|
||||
mTextToSpeech = new TextToSpeech(context, null, null);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
@SuppressWarnings("unused")
|
||||
public boolean isSpeaking() {
|
||||
return mTextToSpeech.isSpeaking();
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
@SuppressWarnings({"unused", "deprecation"})
|
||||
public int speak(String text, int queueMode, String jsonParams) {
|
||||
// Try to pull the params from the JSON string.
|
||||
HashMap<String, String> params = null;
|
||||
try {
|
||||
if (jsonParams != null) {
|
||||
params = new HashMap<String, String>();
|
||||
JSONObject json = new JSONObject(jsonParams);
|
||||
|
||||
// Using legacy API here.
|
||||
@SuppressWarnings("unchecked")
|
||||
Iterator<String> keyIt = json.keys();
|
||||
|
||||
while (keyIt.hasNext()) {
|
||||
String key = keyIt.next();
|
||||
// Only add parameters that are raw data types.
|
||||
if (json.optJSONObject(key) == null && json.optJSONArray(key) == null) {
|
||||
params.put(key, json.getString(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
params = null;
|
||||
}
|
||||
|
||||
return mTextToSpeech.speak(text, queueMode, params);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
@SuppressWarnings("unused")
|
||||
public int stop() {
|
||||
return mTextToSpeech.stop();
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
@SuppressWarnings("unused")
|
||||
public void braille(String jsonString) {
|
||||
// This is here because AndroidVox depends on the existence
|
||||
// of this method.
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
protected void shutdownInternal() {
|
||||
mTextToSpeech.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,268 +0,0 @@
|
||||
// Copyright 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package org.chromium.content.browser.accessibility;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.SystemClock;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import org.chromium.content.browser.ContentViewCore;
|
||||
import org.chromium.content.browser.JavascriptInterface;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* Handles injecting accessibility Javascript and related Javascript -> Java APIs for JB and newer
|
||||
* devices.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
|
||||
class JellyBeanAccessibilityInjector extends AccessibilityInjector {
|
||||
private CallbackHandler mCallback;
|
||||
private JSONObject mAccessibilityJSONObject;
|
||||
|
||||
private static final String ALIAS_TRAVERSAL_JS_INTERFACE = "accessibilityTraversal";
|
||||
|
||||
// Template for JavaScript that performs AndroidVox actions.
|
||||
private static final String ACCESSIBILITY_ANDROIDVOX_TEMPLATE =
|
||||
"cvox.AndroidVox.performAction('%1s')";
|
||||
|
||||
/**
|
||||
* Constructs an instance of the JellyBeanAccessibilityInjector.
|
||||
* @param view The ContentViewCore that this AccessibilityInjector manages.
|
||||
*/
|
||||
protected JellyBeanAccessibilityInjector(ContentViewCore view) {
|
||||
super(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
|
||||
info.setMovementGranularities(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER
|
||||
| AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD
|
||||
| AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE
|
||||
| AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
|
||||
| AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
|
||||
info.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY);
|
||||
info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY);
|
||||
info.addAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT);
|
||||
info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT);
|
||||
info.addAction(AccessibilityNodeInfo.ACTION_CLICK);
|
||||
info.setClickable(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAccessibilityAction(int action) {
|
||||
if (action == AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
|
||||
|| action == AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
|
||||
|| action == AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT
|
||||
|| action == AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT
|
||||
|| action == AccessibilityNodeInfo.ACTION_CLICK) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performAccessibilityAction(int action, Bundle arguments) {
|
||||
if (!accessibilityIsAvailable() || !mContentViewCore.isAlive()
|
||||
|| !mInjectedScriptEnabled || !mScriptInjected) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean actionSuccessful = sendActionToAndroidVox(action, arguments);
|
||||
|
||||
if (actionSuccessful) mContentViewCore.getWebContents().showImeIfNeeded();
|
||||
|
||||
return actionSuccessful;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addAccessibilityApis() {
|
||||
super.addAccessibilityApis();
|
||||
|
||||
Context context = mContentViewCore.getContext();
|
||||
if (context != null && mCallback == null) {
|
||||
mCallback = new CallbackHandler(ALIAS_TRAVERSAL_JS_INTERFACE);
|
||||
mContentViewCore.addJavascriptInterface(mCallback, ALIAS_TRAVERSAL_JS_INTERFACE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeAccessibilityApis() {
|
||||
super.removeAccessibilityApis();
|
||||
|
||||
if (mCallback != null) {
|
||||
mContentViewCore.removeJavascriptInterface(ALIAS_TRAVERSAL_JS_INTERFACE);
|
||||
mCallback = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Packs an accessibility action into a JSON object and sends it to AndroidVox.
|
||||
*
|
||||
* @param action The action identifier.
|
||||
* @param arguments The action arguments, if applicable.
|
||||
* @return The result of the action.
|
||||
*/
|
||||
private boolean sendActionToAndroidVox(int action, Bundle arguments) {
|
||||
if (mCallback == null) return false;
|
||||
if (mAccessibilityJSONObject == null) {
|
||||
mAccessibilityJSONObject = new JSONObject();
|
||||
} else {
|
||||
// Remove all keys from the object.
|
||||
final Iterator<?> keys = mAccessibilityJSONObject.keys();
|
||||
while (keys.hasNext()) {
|
||||
keys.next();
|
||||
keys.remove();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
mAccessibilityJSONObject.accumulate("action", action);
|
||||
if (arguments != null) {
|
||||
if (action == AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY || action
|
||||
== AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY) {
|
||||
final int granularity = arguments.getInt(
|
||||
AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT);
|
||||
mAccessibilityJSONObject.accumulate("granularity", granularity);
|
||||
} else if (action == AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT
|
||||
|| action == AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT) {
|
||||
final String element = arguments.getString(
|
||||
AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING);
|
||||
mAccessibilityJSONObject.accumulate("element", element);
|
||||
}
|
||||
}
|
||||
} catch (JSONException ex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final String jsonString = mAccessibilityJSONObject.toString();
|
||||
final String jsCode = String.format(Locale.US, ACCESSIBILITY_ANDROIDVOX_TEMPLATE,
|
||||
jsonString);
|
||||
return mCallback.performAction(mContentViewCore, jsCode);
|
||||
}
|
||||
|
||||
private static class CallbackHandler {
|
||||
private static final String JAVASCRIPT_ACTION_TEMPLATE = "(function() {"
|
||||
+ " retVal = false;"
|
||||
+ " try {"
|
||||
+ " retVal = %s;"
|
||||
+ " } catch (e) {"
|
||||
+ " retVal = false;"
|
||||
+ " }"
|
||||
+ " %s.onResult(%d, retVal);"
|
||||
+ "})()";
|
||||
|
||||
// Time in milliseconds to wait for a result before failing.
|
||||
private static final long RESULT_TIMEOUT = 5000;
|
||||
|
||||
private final AtomicInteger mResultIdCounter = new AtomicInteger();
|
||||
private final Object mResultLock = new Object();
|
||||
private final String mInterfaceName;
|
||||
|
||||
private boolean mResult = false;
|
||||
private long mResultId = -1;
|
||||
|
||||
private CallbackHandler(String interfaceName) {
|
||||
mInterfaceName = interfaceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an action and attempts to wait for a result.
|
||||
*
|
||||
* @param contentView The ContentViewCore to perform the action on.
|
||||
* @param code Javascript code that evaluates to a result.
|
||||
* @return The result of the action.
|
||||
*/
|
||||
private boolean performAction(ContentViewCore contentView, String code) {
|
||||
final int resultId = mResultIdCounter.getAndIncrement();
|
||||
final String js = String.format(Locale.US, JAVASCRIPT_ACTION_TEMPLATE, code,
|
||||
mInterfaceName, resultId);
|
||||
contentView.getWebContents().evaluateJavaScript(js, null);
|
||||
|
||||
return getResultAndClear(resultId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the result of a request to perform an accessibility action.
|
||||
*
|
||||
* @param resultId The result id to match the result with the request.
|
||||
* @return The result of the request.
|
||||
*/
|
||||
private boolean getResultAndClear(int resultId) {
|
||||
synchronized (mResultLock) {
|
||||
final boolean success = waitForResultTimedLocked(resultId);
|
||||
final boolean result = success ? mResult : false;
|
||||
clearResultLocked();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the result state.
|
||||
*/
|
||||
private void clearResultLocked() {
|
||||
mResultId = -1;
|
||||
mResult = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits up to a given bound for a result of a request and returns it.
|
||||
*
|
||||
* @param resultId The result id to match the result with the request.
|
||||
* @return Whether the result was received.
|
||||
*/
|
||||
private boolean waitForResultTimedLocked(int resultId) {
|
||||
long waitTimeMillis = RESULT_TIMEOUT;
|
||||
final long startTimeMillis = SystemClock.uptimeMillis();
|
||||
while (true) {
|
||||
try {
|
||||
if (mResultId == resultId) return true;
|
||||
if (mResultId > resultId) return false;
|
||||
final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
|
||||
waitTimeMillis = RESULT_TIMEOUT - elapsedTimeMillis;
|
||||
if (waitTimeMillis <= 0) return false;
|
||||
mResultLock.wait(waitTimeMillis);
|
||||
} catch (InterruptedException ie) {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback exposed to JavaScript. Handles returning the result of a
|
||||
* request to a waiting (or potentially timed out) thread.
|
||||
*
|
||||
* @param id The result id of the request as a {@link String}.
|
||||
* @param result The result of a request as a {@link String}.
|
||||
*/
|
||||
@JavascriptInterface
|
||||
@SuppressWarnings("unused")
|
||||
public void onResult(String id, String result) {
|
||||
final long resultId;
|
||||
try {
|
||||
resultId = Long.parseLong(id);
|
||||
} catch (NumberFormatException e) {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (mResultLock) {
|
||||
if (resultId > mResultId) {
|
||||
mResult = Boolean.parseBoolean(result);
|
||||
mResultId = resultId;
|
||||
}
|
||||
mResultLock.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,93 +0,0 @@
|
||||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package org.chromium.content.browser.accessibility;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import org.chromium.content.browser.ContentViewCore;
|
||||
import org.chromium.content.browser.JavascriptInterface;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Handles injecting accessibility Javascript and related Javascript -> Java APIs for Lollipop and
|
||||
* newer devices.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
class LollipopAccessibilityInjector extends JellyBeanAccessibilityInjector {
|
||||
/**
|
||||
* Constructs an instance of the LollipopAccessibilityInjector.
|
||||
* @param view The ContentViewCore that this AccessibilityInjector manages.
|
||||
*/
|
||||
protected LollipopAccessibilityInjector(ContentViewCore view) {
|
||||
super(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
|
||||
info.setMovementGranularities(
|
||||
AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER
|
||||
| AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD
|
||||
| AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE
|
||||
| AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
|
||||
| AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
|
||||
info.addAction(
|
||||
AccessibilityNodeInfo.AccessibilityAction.ACTION_NEXT_AT_MOVEMENT_GRANULARITY);
|
||||
info.addAction(
|
||||
AccessibilityNodeInfo.AccessibilityAction.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY);
|
||||
info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_NEXT_HTML_ELEMENT);
|
||||
info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_PREVIOUS_HTML_ELEMENT);
|
||||
info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK);
|
||||
info.setClickable(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TextToSpeechWrapper createTextToSpeechWrapper(View view, Context context) {
|
||||
return new LTextToSpeechWrapper(view, context);
|
||||
}
|
||||
|
||||
protected static class LTextToSpeechWrapper extends AccessibilityInjector.TextToSpeechWrapper {
|
||||
private LTextToSpeechWrapper(View view, Context context) {
|
||||
super(view, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
@JavascriptInterface
|
||||
@SuppressWarnings("unused")
|
||||
public int speak(String text, int queueMode, String jsonParams) {
|
||||
// Try to pull the params from the JSON string.
|
||||
Bundle bundle = null;
|
||||
try {
|
||||
if (jsonParams != null) {
|
||||
bundle = new Bundle();
|
||||
JSONObject json = new JSONObject(jsonParams);
|
||||
|
||||
// Using legacy API here.
|
||||
@SuppressWarnings("unchecked")
|
||||
Iterator<String> keyIt = json.keys();
|
||||
|
||||
while (keyIt.hasNext()) {
|
||||
String key = keyIt.next();
|
||||
// Only add parameters that are raw data types.
|
||||
if (json.optJSONObject(key) == null && json.optJSONArray(key) == null) {
|
||||
bundle.putCharSequence(key, json.getString(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
bundle = null;
|
||||
}
|
||||
|
||||
return mTextToSpeech.speak(text, queueMode, bundle, null);
|
||||
}
|
||||
}
|
||||
}
|
@ -287,7 +287,7 @@ public class Shell extends LinearLayout {
|
||||
private void initFromNativeTabContents(WebContents webContents) {
|
||||
Context context = getContext();
|
||||
mContentViewCore = new ContentViewCore(context);
|
||||
ContentView cv = ContentView.newInstance(context, mContentViewCore);
|
||||
ContentView cv = new ContentView(context, mContentViewCore);
|
||||
mContentViewCore.initialize(cv, cv, webContents, mWindow);
|
||||
mContentViewCore.setContentViewClient(mContentViewClient);
|
||||
mWebContents = mContentViewCore.getWebContents();
|
||||
|
@ -200,7 +200,7 @@ public class ContentShellTestBase
|
||||
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ContentView cv = ContentView.newInstance(getActivity(), getContentViewCore());
|
||||
ContentView cv = new ContentView(getActivity(), getContentViewCore());
|
||||
((ViewGroup) getContentViewCore().getContainerView().getParent()).addView(cv);
|
||||
getContentViewCore().setContainerView(cv);
|
||||
getContentViewCore().setContainerViewInternals(cv);
|
||||
|
Reference in New Issue
Block a user