Merge "DO NOT MERGE Control access to inherited methods of jsinterface objects" into jb-dev

This commit is contained in:
Geremy Condra
2012-10-09 21:32:08 -07:00
committed by Android (Google) Code Review
5 changed files with 125 additions and 20 deletions

View File

@ -34,6 +34,7 @@ import org.json.JSONObject;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@ -53,7 +54,7 @@ class AccessibilityInjector {
private final WebView mWebView; private final WebView mWebView;
// The Java objects that are exposed to JavaScript. // The Java objects that are exposed to JavaScript.
private TextToSpeech mTextToSpeech; private TextToSpeechWrapper mTextToSpeech;
private CallbackHandler mCallback; private CallbackHandler mCallback;
// Lazily loaded helper objects. // Lazily loaded helper objects.
@ -349,11 +350,8 @@ class AccessibilityInjector {
if (mTextToSpeech != null) { if (mTextToSpeech != null) {
return; return;
} }
mTextToSpeech = new TextToSpeechWrapper(mContext);
final String pkgName = mContext.getPackageName(); mWebViewClassic.addJavascriptInterface(mTextToSpeech, ALIAS_TTS_JS_INTERFACE, false);
mTextToSpeech = new TextToSpeech(mContext, null, null, pkgName + ".**webview**", true);
mWebView.addJavascriptInterface(mTextToSpeech, ALIAS_TTS_JS_INTERFACE);
} }
/** /**
@ -377,7 +375,7 @@ class AccessibilityInjector {
} }
mCallback = new CallbackHandler(ALIAS_TRAVERSAL_JS_INTERFACE); mCallback = new CallbackHandler(ALIAS_TRAVERSAL_JS_INTERFACE);
mWebView.addJavascriptInterface(mCallback, ALIAS_TRAVERSAL_JS_INTERFACE); mWebViewClassic.addJavascriptInterface(mCallback, ALIAS_TRAVERSAL_JS_INTERFACE, false);
} }
private void removeCallbackApis() { private void removeCallbackApis() {
@ -504,9 +502,45 @@ class AccessibilityInjector {
final String jsonString = mAccessibilityJSONObject.toString(); final String jsonString = mAccessibilityJSONObject.toString();
final String jsCode = String.format(ACCESSIBILITY_ANDROIDVOX_TEMPLATE, jsonString); final String jsCode = String.format(ACCESSIBILITY_ANDROIDVOX_TEMPLATE, jsonString);
if (mCallback == null) return false;
return mCallback.performAction(mWebView, jsCode); return mCallback.performAction(mWebView, jsCode);
} }
/**
* Used to protect the TextToSpeech class, only exposing the methods we want to expose.
*/
private static class TextToSpeechWrapper {
private TextToSpeech mTextToSpeech;
public TextToSpeechWrapper(Context context) {
final String pkgName = context.getPackageName();
mTextToSpeech = new TextToSpeech(context, null, null, pkgName + ".**webview**", true);
}
@JavascriptInterface
@SuppressWarnings("unused")
public boolean isSpeaking() {
return mTextToSpeech.isSpeaking();
}
@JavascriptInterface
@SuppressWarnings("unused")
public int speak(String text, int queueMode, HashMap<String, String> params) {
return mTextToSpeech.speak(text, queueMode, params);
}
@JavascriptInterface
@SuppressWarnings("unused")
public int stop() {
return mTextToSpeech.stop();
}
@SuppressWarnings("unused")
protected void shutdown() {
mTextToSpeech.shutdown();
}
}
/** /**
* Exposes result interface to JavaScript. * Exposes result interface to JavaScript.
*/ */
@ -603,6 +637,7 @@ class AccessibilityInjector {
* @param id The result id of the request as a {@link String}. * @param id The result id of the request as a {@link String}.
* @param result The result of the request as a {@link String}. * @param result The result of the request as a {@link String}.
*/ */
@JavascriptInterface
@SuppressWarnings("unused") @SuppressWarnings("unused")
public void onResult(String id, String result) { public void onResult(String id, String result) {
final long resultId; final long resultId;

View File

@ -88,8 +88,19 @@ class BrowserFrame extends Handler {
// Is this frame the main frame? // Is this frame the main frame?
private boolean mIsMainFrame; private boolean mIsMainFrame;
// Javascript interface object
private class JSObject {
Object object;
boolean requireAnnotation;
public JSObject(Object object, boolean requireAnnotation) {
this.object = object;
this.requireAnnotation = requireAnnotation;
}
}
// Attached Javascript interfaces // Attached Javascript interfaces
private Map<String, Object> mJavaScriptObjects; private Map<String, JSObject> mJavaScriptObjects;
private Set<Object> mRemovedJavaScriptObjects; private Set<Object> mRemovedJavaScriptObjects;
// Key store handler when Chromium HTTP stack is used. // Key store handler when Chromium HTTP stack is used.
@ -233,10 +244,8 @@ class BrowserFrame extends Handler {
} }
sConfigCallback.addHandler(this); sConfigCallback.addHandler(this);
mJavaScriptObjects = javascriptInterfaces; mJavaScriptObjects = new HashMap<String, JSObject>();
if (mJavaScriptObjects == null) { addJavaScriptObjects(javascriptInterfaces);
mJavaScriptObjects = new HashMap<String, Object>();
}
mRemovedJavaScriptObjects = new HashSet<Object>(); mRemovedJavaScriptObjects = new HashSet<Object>();
mSettings = settings; mSettings = settings;
@ -590,15 +599,34 @@ class BrowserFrame extends Handler {
Iterator<String> iter = mJavaScriptObjects.keySet().iterator(); Iterator<String> iter = mJavaScriptObjects.keySet().iterator();
while (iter.hasNext()) { while (iter.hasNext()) {
String interfaceName = iter.next(); String interfaceName = iter.next();
Object object = mJavaScriptObjects.get(interfaceName); JSObject jsobject = mJavaScriptObjects.get(interfaceName);
if (object != null) { if (jsobject != null && jsobject.object != null) {
nativeAddJavascriptInterface(nativeFramePointer, nativeAddJavascriptInterface(nativeFramePointer,
mJavaScriptObjects.get(interfaceName), interfaceName); jsobject.object, interfaceName, jsobject.requireAnnotation);
} }
} }
mRemovedJavaScriptObjects.clear(); mRemovedJavaScriptObjects.clear();
} }
/*
* Add javascript objects to the internal list of objects. The default behavior
* is to allow access to inherited methods (no annotation needed). This is only
* used when js objects are passed through a constructor (via a hidden constructor).
*
*/
private void addJavaScriptObjects(Map<String, Object> javascriptInterfaces) {
if (javascriptInterfaces == null) return;
Iterator<String> iter = javascriptInterfaces.keySet().iterator();
while (iter.hasNext()) {
String interfaceName = iter.next();
Object object = javascriptInterfaces.get(interfaceName);
if (object != null) {
mJavaScriptObjects.put(interfaceName, new JSObject(object, false));
}
}
}
/** /**
* This method is called by WebCore to check whether application * This method is called by WebCore to check whether application
* wants to hijack url loading * wants to hijack url loading
@ -616,11 +644,11 @@ class BrowserFrame extends Handler {
} }
} }
public void addJavascriptInterface(Object obj, String interfaceName) { public void addJavascriptInterface(Object obj, String interfaceName,
boolean requireAnnotation) {
assert obj != null; assert obj != null;
removeJavascriptInterface(interfaceName); removeJavascriptInterface(interfaceName);
mJavaScriptObjects.put(interfaceName, new JSObject(obj, requireAnnotation));
mJavaScriptObjects.put(interfaceName, obj);
} }
public void removeJavascriptInterface(String interfaceName) { public void removeJavascriptInterface(String interfaceName) {
@ -1246,7 +1274,7 @@ class BrowserFrame extends Handler {
* Add a javascript interface to the main frame. * Add a javascript interface to the main frame.
*/ */
private native void nativeAddJavascriptInterface(int nativeFramePointer, private native void nativeAddJavascriptInterface(int nativeFramePointer,
Object obj, String interfaceName); Object obj, String interfaceName, boolean requireAnnotation);
public native void clearCache(); public native void clearCache();

View File

@ -0,0 +1,33 @@
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.webkit;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation that allows exposing methods to JavaScript.
*
* @hide
*/
@SuppressWarnings("javadoc")
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface JavascriptInterface {
}

View File

@ -4085,12 +4085,20 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
*/ */
@Override @Override
public void addJavascriptInterface(Object object, String name) { public void addJavascriptInterface(Object object, String name) {
addJavascriptInterface(object, name, false);
}
/**
* @hide
*/
public void addJavascriptInterface(Object object, String name, boolean requireAnnotation) {
if (object == null) { if (object == null) {
return; return;
} }
WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData(); WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
arg.mObject = object; arg.mObject = object;
arg.mInterfaceName = name; arg.mInterfaceName = name;
arg.mRequireAnnotation = requireAnnotation;
mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg); mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg);
} }

View File

@ -839,6 +839,7 @@ public final class WebViewCore {
static class JSInterfaceData { static class JSInterfaceData {
Object mObject; Object mObject;
String mInterfaceName; String mInterfaceName;
boolean mRequireAnnotation;
} }
static class JSKeyData { static class JSKeyData {
@ -1508,7 +1509,7 @@ public final class WebViewCore {
case ADD_JS_INTERFACE: case ADD_JS_INTERFACE:
JSInterfaceData jsData = (JSInterfaceData) msg.obj; JSInterfaceData jsData = (JSInterfaceData) msg.obj;
mBrowserFrame.addJavascriptInterface(jsData.mObject, mBrowserFrame.addJavascriptInterface(jsData.mObject,
jsData.mInterfaceName); jsData.mInterfaceName, jsData.mRequireAnnotation);
break; break;
case REMOVE_JS_INTERFACE: case REMOVE_JS_INTERFACE: