0

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:
aurimas
2015-04-22 18:14:39 -07:00
committed by Commit bot
parent 777ee88e54
commit eeffed48a0
18 changed files with 39 additions and 1094 deletions
android_webview
java
src
org
test
shell
src
org
chromium
android_webview
chrome/android
java
javatests
src
org
chromium
shell
java
src
org
chromecast/browser/android/apk/src/org/chromium/chromecast/shell
content

@ -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,

@ -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);