diff options
| -rw-r--r-- | core/java/android/webkit/AccessibilityInjector.java | 49 | ||||
| -rw-r--r-- | core/java/android/webkit/BrowserFrame.java | 52 | ||||
| -rw-r--r-- | core/java/android/webkit/JavascriptInterface.java | 33 | ||||
| -rw-r--r-- | core/java/android/webkit/WebViewClassic.java | 8 | ||||
| -rw-r--r-- | core/java/android/webkit/WebViewCore.java | 3 |
5 files changed, 125 insertions, 20 deletions
diff --git a/core/java/android/webkit/AccessibilityInjector.java b/core/java/android/webkit/AccessibilityInjector.java index 7dfb5bb102e2..c32359f5f2ec 100644 --- a/core/java/android/webkit/AccessibilityInjector.java +++ b/core/java/android/webkit/AccessibilityInjector.java @@ -34,6 +34,7 @@ import org.json.JSONObject; import java.net.URI; import java.net.URISyntaxException; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @@ -53,7 +54,7 @@ class AccessibilityInjector { private final WebView mWebView; // The Java objects that are exposed to JavaScript. - private TextToSpeech mTextToSpeech; + private TextToSpeechWrapper mTextToSpeech; private CallbackHandler mCallback; // Lazily loaded helper objects. @@ -349,11 +350,8 @@ class AccessibilityInjector { if (mTextToSpeech != null) { return; } - - final String pkgName = mContext.getPackageName(); - - mTextToSpeech = new TextToSpeech(mContext, null, null, pkgName + ".**webview**", true); - mWebView.addJavascriptInterface(mTextToSpeech, ALIAS_TTS_JS_INTERFACE); + mTextToSpeech = new TextToSpeechWrapper(mContext); + mWebViewClassic.addJavascriptInterface(mTextToSpeech, ALIAS_TTS_JS_INTERFACE, false); } /** @@ -377,7 +375,7 @@ class AccessibilityInjector { } 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() { @@ -504,10 +502,46 @@ class AccessibilityInjector { final String jsonString = mAccessibilityJSONObject.toString(); final String jsCode = String.format(ACCESSIBILITY_ANDROIDVOX_TEMPLATE, jsonString); + if (mCallback == null) return false; 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. */ private static class CallbackHandler { @@ -603,6 +637,7 @@ class AccessibilityInjector { * @param id The result id of the request as a {@link String}. * @param result The result of the request as a {@link String}. */ + @JavascriptInterface @SuppressWarnings("unused") public void onResult(String id, String result) { final long resultId; diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java index e52eb6e80cf0..9e454a7b1934 100644 --- a/core/java/android/webkit/BrowserFrame.java +++ b/core/java/android/webkit/BrowserFrame.java @@ -88,8 +88,19 @@ class BrowserFrame extends Handler { // Is this frame the main frame? 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 - private Map<String, Object> mJavaScriptObjects; + private Map<String, JSObject> mJavaScriptObjects; private Set<Object> mRemovedJavaScriptObjects; // Key store handler when Chromium HTTP stack is used. @@ -233,10 +244,8 @@ class BrowserFrame extends Handler { } sConfigCallback.addHandler(this); - mJavaScriptObjects = javascriptInterfaces; - if (mJavaScriptObjects == null) { - mJavaScriptObjects = new HashMap<String, Object>(); - } + mJavaScriptObjects = new HashMap<String, JSObject>(); + addJavaScriptObjects(javascriptInterfaces); mRemovedJavaScriptObjects = new HashSet<Object>(); mSettings = settings; @@ -590,15 +599,34 @@ class BrowserFrame extends Handler { Iterator<String> iter = mJavaScriptObjects.keySet().iterator(); while (iter.hasNext()) { String interfaceName = iter.next(); - Object object = mJavaScriptObjects.get(interfaceName); - if (object != null) { + JSObject jsobject = mJavaScriptObjects.get(interfaceName); + if (jsobject != null && jsobject.object != null) { nativeAddJavascriptInterface(nativeFramePointer, - mJavaScriptObjects.get(interfaceName), interfaceName); + jsobject.object, interfaceName, jsobject.requireAnnotation); } } 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 * 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; removeJavascriptInterface(interfaceName); - - mJavaScriptObjects.put(interfaceName, obj); + mJavaScriptObjects.put(interfaceName, new JSObject(obj, requireAnnotation)); } public void removeJavascriptInterface(String interfaceName) { @@ -1246,7 +1274,7 @@ class BrowserFrame extends Handler { * Add a javascript interface to the main frame. */ private native void nativeAddJavascriptInterface(int nativeFramePointer, - Object obj, String interfaceName); + Object obj, String interfaceName, boolean requireAnnotation); public native void clearCache(); diff --git a/core/java/android/webkit/JavascriptInterface.java b/core/java/android/webkit/JavascriptInterface.java new file mode 100644 index 000000000000..a5312a2c0bf0 --- /dev/null +++ b/core/java/android/webkit/JavascriptInterface.java @@ -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 { +}
\ No newline at end of file diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java index 501db7d9f3ac..d1d867c6735f 100644 --- a/core/java/android/webkit/WebViewClassic.java +++ b/core/java/android/webkit/WebViewClassic.java @@ -4085,12 +4085,20 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc */ @Override public void addJavascriptInterface(Object object, String name) { + addJavascriptInterface(object, name, false); + } + + /** + * @hide + */ + public void addJavascriptInterface(Object object, String name, boolean requireAnnotation) { if (object == null) { return; } WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData(); arg.mObject = object; arg.mInterfaceName = name; + arg.mRequireAnnotation = requireAnnotation; mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg); } diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 728ddbfc9fe1..ae49202806a6 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -839,6 +839,7 @@ public final class WebViewCore { static class JSInterfaceData { Object mObject; String mInterfaceName; + boolean mRequireAnnotation; } static class JSKeyData { @@ -1508,7 +1509,7 @@ public final class WebViewCore { case ADD_JS_INTERFACE: JSInterfaceData jsData = (JSInterfaceData) msg.obj; mBrowserFrame.addJavascriptInterface(jsData.mObject, - jsData.mInterfaceName); + jsData.mInterfaceName, jsData.mRequireAnnotation); break; case REMOVE_JS_INTERFACE: |