DO NOT MERGE Control access to inherited methods of jsinterface objects

Bug: 7073422

Use a flag and annotation for allowing access to inherited methods of
jsinterface objects. When flag is false, no annotation is needed. When
flag is true, annotation is needed for allowing access to inherited methods.

Clean cherry pick of f2d8c5bed31609d7d6e3ae77f33e90ea7f888eb3

Change-Id: I226ef92e75df49293f6543abfcd17895abe76adc
diff --git a/Source/WebCore/bridge/jni/v8/JNIUtilityPrivate.cpp b/Source/WebCore/bridge/jni/v8/JNIUtilityPrivate.cpp
index 15b4bda..c6e0a6c 100644
--- a/Source/WebCore/bridge/jni/v8/JNIUtilityPrivate.cpp
+++ b/Source/WebCore/bridge/jni/v8/JNIUtilityPrivate.cpp
@@ -206,7 +206,7 @@
                 break;
             }
 
-            result.m_objectValue = adoptRef(new JavaInstanceJobject(javaArray));
+            result.m_objectValue = adoptRef(new JavaInstanceJobject(javaArray, false));
             env->DeleteLocalRef(javaArray);
         }
         break;
@@ -421,7 +421,11 @@
     }
 }
 
+#if PLATFORM(ANDROID)
+JavaValue jvalueToJavaValue(const jvalue& value, const JavaType& type, bool requireAnnotation)
+#else
 JavaValue jvalueToJavaValue(const jvalue& value, const JavaType& type)
+#endif
 {
     JavaValue result;
     result.m_type = type;
@@ -429,7 +433,11 @@
     case JavaTypeVoid:
         break;
     case JavaTypeObject:
+#if PLATFORM(ANDROID)
+        result.m_objectValue = new JavaInstanceJobject(value.l, requireAnnotation);
+#else
         result.m_objectValue = new JavaInstanceJobject(value.l);
+#endif
         break;
     case JavaTypeString:
         {
diff --git a/Source/WebCore/bridge/jni/v8/JNIUtilityPrivate.h b/Source/WebCore/bridge/jni/v8/JNIUtilityPrivate.h
index 0282904..47ee45a 100644
--- a/Source/WebCore/bridge/jni/v8/JNIUtilityPrivate.h
+++ b/Source/WebCore/bridge/jni/v8/JNIUtilityPrivate.h
@@ -41,7 +41,11 @@
 JavaValue convertNPVariantToJavaValue(NPVariant, const String& javaClass);
 void convertJavaValueToNPVariant(JavaValue, NPVariant*);
 
+#if PLATFORM(ANDROID)
+JavaValue jvalueToJavaValue(const jvalue&, const JavaType&, bool requireAnnotation);
+#else
 JavaValue jvalueToJavaValue(const jvalue&, const JavaType&);
+#endif
 jvalue javaValueToJvalue(const JavaValue&);
 
 } // namespace Bindings
diff --git a/Source/WebCore/bridge/jni/v8/JavaClassJobjectV8.cpp b/Source/WebCore/bridge/jni/v8/JavaClassJobjectV8.cpp
index 40bfd63..669eb49 100644
--- a/Source/WebCore/bridge/jni/v8/JavaClassJobjectV8.cpp
+++ b/Source/WebCore/bridge/jni/v8/JavaClassJobjectV8.cpp
@@ -33,7 +33,18 @@
 
 using namespace JSC::Bindings;
 
+#if PLATFORM(ANDROID)
+const char kJavaScriptInterfaceAnnotation[] = "android/webkit/JavascriptInterface";
+const char kIsAnnotationPresent[] = "isAnnotationPresent";
+const char kGetMethods[] = "getMethods";
+
+static jclass safeAnnotationClazz = NULL;
+
+JavaClassJobject::JavaClassJobject(jobject anInstance, bool requireAnnotation)
+    : m_requireAnnotation(requireAnnotation)
+#else
 JavaClassJobject::JavaClassJobject(jobject anInstance)
+#endif
 {
     jobject aClass = callJNIMethod<jobject>(anInstance, "getClass", "()Ljava/lang/Class;");
 
@@ -57,15 +68,28 @@
     // Get the methods
     jarray methods = static_cast<jarray>(callJNIMethod<jobject>(aClass, "getMethods", "()[Ljava/lang/reflect/Method;"));
     int numMethods = env->GetArrayLength(methods);
+#if PLATFORM(ANDROID)
+    jmethodID isAnnotationPresentMethodID = getAnnotationMethodID(env);
+    if (!isAnnotationPresentMethodID) {
+        LOG_ERROR("unable to find method %s on instance %p", kIsAnnotationPresent, anInstance);
+        return;
+    }
+#endif
     for (int i = 0; i < numMethods; i++) {
         jobject aJMethod = env->GetObjectArrayElement(static_cast<jobjectArray>(methods), i);
-        JavaMethod* aMethod = new JavaMethodJobject(env, aJMethod); // deleted in the JavaClass destructor
-        MethodList* methodList = m_methods.get(aMethod->name());
-        if (!methodList) {
-            methodList = new MethodList();
-            m_methods.set(aMethod->name(), methodList);
+#if PLATFORM(ANDROID)
+        if (jsAccessAllowed(env, isAnnotationPresentMethodID, aJMethod)) {
+#endif
+            JavaMethod* aMethod = new JavaMethodJobject(env, aJMethod); // deleted in the JavaClass destructor
+            MethodList* methodList = m_methods.get(aMethod->name());
+            if (!methodList) {
+                methodList = new MethodList();
+                m_methods.set(aMethod->name(), methodList);
+            }
+            methodList->append(aMethod);
+#if PLATFORM(ANDROID)
         }
-        methodList->append(aMethod);
+#endif
         env->DeleteLocalRef(aJMethod);
     }
     env->DeleteLocalRef(fields);
@@ -87,6 +111,44 @@
     m_methods.clear();
 }
 
+#if PLATFORM(ANDROID)
+bool JavaClassJobject::jsAccessAllowed(JNIEnv* env, jmethodID mid, jobject aJMethod)
+{
+    if (!m_requireAnnotation)
+        return true;
+    bool accessAllowed = env->CallBooleanMethod(aJMethod, mid, safeAnnotationClazz);
+    if (env->ExceptionCheck()) {
+        env->ExceptionDescribe();
+        env->ExceptionClear();
+        return false;
+    }
+    return accessAllowed;
+}
+
+jmethodID JavaClassJobject::getAnnotationMethodID(JNIEnv* env)
+{
+    jclass methodClass = env->FindClass("java/lang/reflect/Method");
+    jmethodID mid = 0;
+    if (methodClass)
+        mid = env->GetMethodID(methodClass, kIsAnnotationPresent, "(Ljava/lang/Class;)Z");
+    if (!methodClass || !mid) {
+        env->ExceptionDescribe();
+        env->ExceptionClear();
+    }
+    env->DeleteLocalRef(methodClass);
+    return mid;
+}
+
+bool JavaClassJobject::RegisterJavaClassJobject(JNIEnv* env) {
+    safeAnnotationClazz = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(kJavaScriptInterfaceAnnotation)));
+    if (!safeAnnotationClazz) {
+        LOG_ERROR("failed to register %s", kJavaScriptInterfaceAnnotation);
+        return false;
+    }
+    return true;
+}
+#endif
+
 MethodList JavaClassJobject::methodsNamed(const char* name) const
 {
     MethodList* methodList = m_methods.get(name);
diff --git a/Source/WebCore/bridge/jni/v8/JavaClassJobjectV8.h b/Source/WebCore/bridge/jni/v8/JavaClassJobjectV8.h
index d27ca97..e4035aa 100644
--- a/Source/WebCore/bridge/jni/v8/JavaClassJobjectV8.h
+++ b/Source/WebCore/bridge/jni/v8/JavaClassJobjectV8.h
@@ -37,17 +37,33 @@
 
 class JavaClassJobject : public JavaClass {
 public:
+#if PLATFORM(ANDROID)
+    JavaClassJobject(jobject, bool);
+#else
     JavaClassJobject(jobject);
+#endif
     virtual ~JavaClassJobject();
 
     // JavaClass implementation
     virtual MethodList methodsNamed(const char* name) const;
     virtual JavaField* fieldNamed(const char* name) const;
 
+#if PLATFORM(ANDROID)
+    static bool RegisterJavaClassJobject(JNIEnv* env);
+#endif
+
 private:
+#if PLATFORM(ANDROID)
+    bool jsAccessAllowed(JNIEnv* env, jmethodID mid, jobject aJMethod);
+    jmethodID getAnnotationMethodID(JNIEnv* env);
+#endif
+
     typedef HashMap<WTF::String, MethodList*> MethodListMap;
     MethodListMap m_methods;
     FieldMap m_fields;
+#if PLATFORM(ANDROID)
+    bool m_requireAnnotation;
+#endif
 };
 
 } // namespace Bindings
diff --git a/Source/WebCore/bridge/jni/v8/JavaInstanceJobjectV8.cpp b/Source/WebCore/bridge/jni/v8/JavaInstanceJobjectV8.cpp
index e7b854d..c97a67f 100644
--- a/Source/WebCore/bridge/jni/v8/JavaInstanceJobjectV8.cpp
+++ b/Source/WebCore/bridge/jni/v8/JavaInstanceJobjectV8.cpp
@@ -41,8 +41,15 @@
 
 using namespace JSC::Bindings;
 
+#if PLATFORM(ANDROID)
+JavaInstanceJobject::JavaInstanceJobject(jobject instance, bool requireAnnotation)
+#else
 JavaInstanceJobject::JavaInstanceJobject(jobject instance)
+#endif
     : m_instance(new JobjectWrapper(instance))
+#if PLATFORM(ANDROID)
+    , m_requireAnnotation(requireAnnotation)
+#endif
 {
 }
 
@@ -61,7 +68,11 @@
 JavaClass* JavaInstanceJobject::getClass() const
 {
     if (!m_class)
+#if PLATFORM(ANDROID)
+        m_class = adoptPtr(new JavaClassJobject(javaInstance(), m_requireAnnotation));
+#else
         m_class = adoptPtr(new JavaClassJobject(javaInstance()));
+#endif
     return m_class.get();
 }
 
@@ -86,7 +97,7 @@
         return JavaValue();
     }
 
-    return jvalueToJavaValue(result, method->returnType());
+    return jvalueToJavaValue(result, method->returnType(), m_requireAnnotation);
 // END ANDROID
 }
 
@@ -113,7 +124,11 @@
         appendClassName(signature, field->typeClassName());
         signature.append(';');
     }
+#if PLATFORM(ANDROID)
+    return jvalueToJavaValue(getJNIField(javaInstance(), field->type(), field->name().utf8().data(), signature.toString().utf8().data()), field->type(), m_requireAnnotation);
+#else
     return jvalueToJavaValue(getJNIField(javaInstance(), field->type(), field->name().utf8().data(), signature.toString().utf8().data()), field->type());
+#endif
 }
 
 #endif // ENABLE(JAVA_BRIDGE)
diff --git a/Source/WebCore/bridge/jni/v8/JavaInstanceJobjectV8.h b/Source/WebCore/bridge/jni/v8/JavaInstanceJobjectV8.h
index 255c190..77dcf28 100644
--- a/Source/WebCore/bridge/jni/v8/JavaInstanceJobjectV8.h
+++ b/Source/WebCore/bridge/jni/v8/JavaInstanceJobjectV8.h
@@ -48,8 +48,11 @@
 
 class JavaInstanceJobject : public JavaInstance {
 public:
+#if PLATFORM(ANDROID)
+    JavaInstanceJobject(jobject instance, bool requireAnnotation);
+#else
     JavaInstanceJobject(jobject instance);
-
+#endif
     // JavaInstance implementation
     virtual JavaClass* getClass() const;
 // ANDROID
@@ -64,6 +67,9 @@
 protected:
     RefPtr<JobjectWrapper> m_instance;
     mutable OwnPtr<JavaClass> m_class;
+#if PLATFORM(ANDROID)
+    bool m_requireAnnotation;
+#endif
 };
 
 } // namespace Bindings
diff --git a/Source/WebKit/android/jni/WebCoreFrameBridge.cpp b/Source/WebKit/android/jni/WebCoreFrameBridge.cpp
index 4ce3d8e..16d397a 100644
--- a/Source/WebKit/android/jni/WebCoreFrameBridge.cpp
+++ b/Source/WebKit/android/jni/WebCoreFrameBridge.cpp
@@ -60,6 +60,7 @@
 #include "IconDatabase.h"
 #include "Image.h"
 #include "InspectorClientAndroid.h"
+#include "JavaClassJobjectV8.h"
 #include "JavaNPObjectV8.h"
 #include "JavaInstanceJobjectV8.h"
 #include "KURL.h"
@@ -1505,14 +1506,14 @@
 // and virtualEnd and swap the weak reference for the real object.
 class WeakJavaInstance : public JavaInstanceJobject {
 public:
-    static PassRefPtr<WeakJavaInstance> create(jobject obj)
+    static PassRefPtr<WeakJavaInstance> create(jobject obj, bool requireAnnotation)
     {
-        return adoptRef(new WeakJavaInstance(obj));
+        return adoptRef(new WeakJavaInstance(obj, requireAnnotation));
     }
 
 private:
-    WeakJavaInstance(jobject instance)
-        : JavaInstanceJobject(instance)
+    WeakJavaInstance(jobject instance, bool requireAnnotation)
+        : JavaInstanceJobject(instance, requireAnnotation)
         , m_beginEndDepth(0)
     {
         JNIEnv* env = getJNIEnv();
@@ -1569,7 +1570,7 @@
 };
 
 static void AddJavascriptInterface(JNIEnv *env, jobject obj, jint nativeFramePointer,
-        jobject javascriptObj, jstring interfaceName)
+        jobject javascriptObj, jstring interfaceName, jboolean requireAnnotation)
 {
     WebCore::Frame* pFrame = 0;
     if (nativeFramePointer == 0)
@@ -1583,7 +1584,8 @@
     ALOGV("::WebCore:: addJSInterface: %p", pFrame);
 
     if (pFrame) {
-        RefPtr<JavaInstance> addedObject = WeakJavaInstance::create(javascriptObj);
+        RefPtr<JavaInstance> addedObject = WeakJavaInstance::create(javascriptObj,
+                requireAnnotation);
         const char* name = getCharactersFromJStringInEnv(env, interfaceName);
         // Pass ownership of the added object to bindToWindowObject.
         NPObject* npObject = JavaInstanceToNPObject(addedObject.get());
@@ -1951,7 +1953,7 @@
         (void*) Reload },
     { "nativeGoBackOrForward", "(I)V",
         (void*) GoBackOrForward },
-    { "nativeAddJavascriptInterface", "(ILjava/lang/Object;Ljava/lang/String;)V",
+    { "nativeAddJavascriptInterface", "(ILjava/lang/Object;Ljava/lang/String;Z)V",
         (void*) AddJavascriptInterface },
     { "stringByEvaluatingJavaScriptFromString",
             "(Ljava/lang/String;)Ljava/lang/String;",
@@ -1986,6 +1988,8 @@
 
 int registerWebFrame(JNIEnv* env)
 {
+    JavaClassJobject::RegisterJavaClassJobject(env);
+
     jclass clazz = env->FindClass("android/webkit/BrowserFrame");
     ALOG_ASSERT(clazz, "Cannot find BrowserFrame");
     gFrameField = env->GetFieldID(clazz, "mNativeFrame", "I");