Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 534a67c5 authored by Selim Gurun's avatar Selim Gurun
Browse files

DO NOT MERGE Control access to inherited methods of jsinterface objects

Bug: 7073422

Create the plumbing to use an annotation to allow access to
inherited methods of jsinterface objects. The default webview
behavior has not changed yet. However internally an a flag is
introduced to restrict javascript access to methods that have an annotation.

Clean cherry pick of 94740e6c

Provided @JavascriptInterface to methods that are accessible from
js.

Clean cherry pick of b743a23f

Add a glue logic to require use of annotations in injected accessibility
 objects.

Change-Id: I4135bd6787b2084177215302cd2c72afed090dc0
parent 8d197601
Loading
Loading
Loading
Loading
+42 −7
Original line number Diff line number Diff line
@@ -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,9 +502,45 @@ 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.
     */
@@ -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;
+40 −12
Original line number Diff line number Diff line
@@ -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();

+33 −0
Original line number Diff line number Diff line
/*
 * 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
+8 −0
Original line number Diff line number Diff line
@@ -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);
    }

+2 −1
Original line number Diff line number Diff line
@@ -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: