/* * Copyright 2006, The Android Open Source Project * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define LOG_TAG "webcoreglue" #include "config.h" #include "MemoryCache.h" #include "Connection.h" #include "CookieClient.h" #include "FileSystemClient.h" #include "JavaSharedClient.h" #include "KeyGeneratorClient.h" #include "KURL.h" #include "NetworkStateNotifier.h" #include "PackageNotifier.h" #include "Page.h" #include "PluginClient.h" #include "PluginDatabase.h" #include "Timer.h" #include "TimerClient.h" #include "WebCache.h" #include "WebCoreJni.h" #include #include #include #include #include #include #include #include namespace android { // ---------------------------------------------------------------------------- static jfieldID gJavaBridge_ObjectID; // ---------------------------------------------------------------------------- class JavaBridge : public TimerClient, public CookieClient, public PluginClient, public KeyGeneratorClient, public FileSystemClient { public: JavaBridge(JNIEnv* env, jobject obj); virtual ~JavaBridge(); /* * WebCore -> Java API */ virtual void setSharedTimer(long long timemillis); virtual void stopSharedTimer(); virtual void setCookies(WebCore::KURL const& url, WTF::String const& value); virtual WTF::String cookies(WebCore::KURL const& url); virtual bool cookiesEnabled(); virtual WTF::Vector getPluginDirectories(); virtual WTF::String getPluginSharedDataDirectory(); virtual WTF::Vector getSupportedKeyStrengthList(); virtual WTF::String getSignedPublicKeyAndChallengeString(unsigned index, const WTF::String& challenge, const WebCore::KURL& url); virtual WTF::String resolveFilePathForContentUri(const WTF::String& uri); //////////////////////////////////////////// virtual void setSharedTimerCallback(void (*f)()); //////////////////////////////////////////// virtual void signalServiceFuncPtrQueue(); // jni functions static void Constructor(JNIEnv* env, jobject obj); static void Finalize(JNIEnv* env, jobject obj); static void SharedTimerFired(JNIEnv* env, jobject); static void SetCacheSize(JNIEnv* env, jobject obj, jint bytes); static void SetNetworkOnLine(JNIEnv* env, jobject obj, jboolean online); static void SetNetworkType(JNIEnv* env, jobject obj, jstring type, jstring subtype); static void SetDeferringTimers(JNIEnv* env, jobject obj, jboolean defer); static void ServiceFuncPtrQueue(JNIEnv*); static void UpdatePluginDirectories(JNIEnv* env, jobject obj, jobjectArray array, jboolean reload); static void AddPackageNames(JNIEnv* env, jobject obj, jobject packageNames); static void AddPackageName(JNIEnv* env, jobject obj, jstring packageName); static void RemovePackageName(JNIEnv* env, jobject obj, jstring packageName); static void UpdateProxy(JNIEnv* env, jobject obj, jstring newProxy, jstring newExList); private: jweak mJavaObject; jmethodID mSetSharedTimer; jmethodID mStopSharedTimer; jmethodID mSetCookies; jmethodID mCookies; jmethodID mCookiesEnabled; jmethodID mGetPluginDirectories; jmethodID mGetPluginSharedDataDirectory; jmethodID mSignalFuncPtrQueue; jmethodID mGetKeyStrengthList; jmethodID mGetSignedPublicKey; jmethodID mResolveFilePathForContentUri; AutoJObject javaObject(JNIEnv* env) { return getRealObject(env, mJavaObject); } }; static void (*sSharedTimerFiredCallback)(); JavaBridge::JavaBridge(JNIEnv* env, jobject obj) { mJavaObject = env->NewWeakGlobalRef(obj); jclass clazz = env->GetObjectClass(obj); mSetSharedTimer = env->GetMethodID(clazz, "setSharedTimer", "(J)V"); mStopSharedTimer = env->GetMethodID(clazz, "stopSharedTimer", "()V"); mSetCookies = env->GetMethodID(clazz, "setCookies", "(Ljava/lang/String;Ljava/lang/String;)V"); mCookies = env->GetMethodID(clazz, "cookies", "(Ljava/lang/String;)Ljava/lang/String;"); mCookiesEnabled = env->GetMethodID(clazz, "cookiesEnabled", "()Z"); mGetPluginDirectories = env->GetMethodID(clazz, "getPluginDirectories", "()[Ljava/lang/String;"); mGetPluginSharedDataDirectory = env->GetMethodID(clazz, "getPluginSharedDataDirectory", "()Ljava/lang/String;"); mSignalFuncPtrQueue = env->GetMethodID(clazz, "signalServiceFuncPtrQueue", "()V"); mGetKeyStrengthList = env->GetMethodID(clazz, "getKeyStrengthList", "()[Ljava/lang/String;"); mGetSignedPublicKey = env->GetMethodID(clazz, "getSignedPublicKey", "(ILjava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); mResolveFilePathForContentUri = env->GetMethodID(clazz, "resolveFilePathForContentUri", "(Ljava/lang/String;)Ljava/lang/String;"); env->DeleteLocalRef(clazz); ALOG_ASSERT(mSetSharedTimer, "Could not find method setSharedTimer"); ALOG_ASSERT(mStopSharedTimer, "Could not find method stopSharedTimer"); ALOG_ASSERT(mSetCookies, "Could not find method setCookies"); ALOG_ASSERT(mCookies, "Could not find method cookies"); ALOG_ASSERT(mCookiesEnabled, "Could not find method cookiesEnabled"); ALOG_ASSERT(mGetPluginDirectories, "Could not find method getPluginDirectories"); ALOG_ASSERT(mGetPluginSharedDataDirectory, "Could not find method getPluginSharedDataDirectory"); ALOG_ASSERT(mGetKeyStrengthList, "Could not find method getKeyStrengthList"); ALOG_ASSERT(mGetSignedPublicKey, "Could not find method getSignedPublicKey"); JavaSharedClient::SetTimerClient(this); JavaSharedClient::SetCookieClient(this); JavaSharedClient::SetPluginClient(this); JavaSharedClient::SetKeyGeneratorClient(this); JavaSharedClient::SetFileSystemClient(this); } JavaBridge::~JavaBridge() { if (mJavaObject) { JNIEnv* env = JSC::Bindings::getJNIEnv(); env->DeleteWeakGlobalRef(mJavaObject); mJavaObject = 0; } JavaSharedClient::SetTimerClient(NULL); JavaSharedClient::SetCookieClient(NULL); JavaSharedClient::SetPluginClient(NULL); JavaSharedClient::SetKeyGeneratorClient(NULL); JavaSharedClient::SetFileSystemClient(NULL); } void JavaBridge::setSharedTimer(long long timemillis) { JNIEnv* env = JSC::Bindings::getJNIEnv(); AutoJObject obj = javaObject(env); if (!obj.get()) return; env->CallVoidMethod(obj.get(), mSetSharedTimer, timemillis); } void JavaBridge::stopSharedTimer() { JNIEnv* env = JSC::Bindings::getJNIEnv(); AutoJObject obj = javaObject(env); if (!obj.get()) return; env->CallVoidMethod(obj.get(), mStopSharedTimer); } void JavaBridge::setCookies(WebCore::KURL const& url, WTF::String const& value) { JNIEnv* env = JSC::Bindings::getJNIEnv(); AutoJObject obj = javaObject(env); if (!obj.get()) return; const WTF::String& urlStr = url.string(); jstring jUrlStr = wtfStringToJstring(env, urlStr); jstring jValueStr = wtfStringToJstring(env, value); env->CallVoidMethod(obj.get(), mSetCookies, jUrlStr, jValueStr); env->DeleteLocalRef(jUrlStr); env->DeleteLocalRef(jValueStr); } WTF::String JavaBridge::cookies(WebCore::KURL const& url) { JNIEnv* env = JSC::Bindings::getJNIEnv(); AutoJObject obj = javaObject(env); if (!obj.get()) return String(); const WTF::String& urlStr = url.string(); jstring jUrlStr = wtfStringToJstring(env, urlStr); jstring string = (jstring)(env->CallObjectMethod(obj.get(), mCookies, jUrlStr)); WTF::String ret = jstringToWtfString(env, string); env->DeleteLocalRef(jUrlStr); env->DeleteLocalRef(string); return ret; } bool JavaBridge::cookiesEnabled() { JNIEnv* env = JSC::Bindings::getJNIEnv(); AutoJObject obj = javaObject(env); if (!obj.get()) return false; jboolean ret = env->CallBooleanMethod(obj.get(), mCookiesEnabled); return (ret != 0); } WTF::Vector JavaBridge::getPluginDirectories() { WTF::Vector directories; JNIEnv* env = JSC::Bindings::getJNIEnv(); AutoJObject obj = javaObject(env); if (!obj.get()) return directories; jobjectArray array = (jobjectArray) env->CallObjectMethod(obj.get(), mGetPluginDirectories); int count = env->GetArrayLength(array); for (int i = 0; i < count; i++) { jstring dir = (jstring) env->GetObjectArrayElement(array, i); directories.append(jstringToWtfString(env, dir)); env->DeleteLocalRef(dir); } env->DeleteLocalRef(array); checkException(env); return directories; } WTF::String JavaBridge::getPluginSharedDataDirectory() { JNIEnv* env = JSC::Bindings::getJNIEnv(); AutoJObject obj = javaObject(env); if (!obj.get()) return String(); jstring ret = (jstring)env->CallObjectMethod(obj.get(), mGetPluginSharedDataDirectory); WTF::String path = jstringToWtfString(env, ret); checkException(env); return path; } void JavaBridge::setSharedTimerCallback(void (*f)()) { ALOG_ASSERT(!sSharedTimerFiredCallback || sSharedTimerFiredCallback==f, "Shared timer callback may already be set or null!"); sSharedTimerFiredCallback = f; } void JavaBridge::signalServiceFuncPtrQueue() { // In order to signal the main thread we must go through JNI. This // is the only usage on most threads, so we need to ensure a JNI // environment is setup. JNIEnv* env = JSC::Bindings::getJNIEnv(); AutoJObject obj = javaObject(env); if (!obj.get()) return; env->CallVoidMethod(obj.get(), mSignalFuncPtrQueue); } WTF::VectorJavaBridge::getSupportedKeyStrengthList() { WTF::Vector list; JNIEnv* env = JSC::Bindings::getJNIEnv(); AutoJObject obj = javaObject(env); if (!obj.get()) return list; jobjectArray array = (jobjectArray) env->CallObjectMethod(obj.get(), mGetKeyStrengthList); int count = env->GetArrayLength(array); for (int i = 0; i < count; ++i) { jstring keyStrength = (jstring) env->GetObjectArrayElement(array, i); list.append(jstringToWtfString(env, keyStrength)); env->DeleteLocalRef(keyStrength); } env->DeleteLocalRef(array); checkException(env); return list; } WTF::String JavaBridge::getSignedPublicKeyAndChallengeString(unsigned index, const WTF::String& challenge, const WebCore::KURL& url) { JNIEnv* env = JSC::Bindings::getJNIEnv(); AutoJObject obj = javaObject(env); if (!obj.get()) return String(); jstring jChallenge = wtfStringToJstring(env, challenge); const WTF::String& urlStr = url.string(); jstring jUrl = wtfStringToJstring(env, urlStr); jstring key = (jstring) env->CallObjectMethod(obj.get(), mGetSignedPublicKey, index, jChallenge, jUrl); WTF::String ret = jstringToWtfString(env, key); env->DeleteLocalRef(jChallenge); env->DeleteLocalRef(jUrl); env->DeleteLocalRef(key); return ret; } WTF::String JavaBridge::resolveFilePathForContentUri(const WTF::String& uri) { JNIEnv* env = JSC::Bindings::getJNIEnv(); AutoJObject obj = javaObject(env); if (!obj.get()) return String(); jstring jUri = wtfStringToJstring(env, uri); jstring path = static_cast(env->CallObjectMethod(obj.get(), mResolveFilePathForContentUri, jUri)); WTF::String ret = jstringToWtfString(env, path); env->DeleteLocalRef(jUri); env->DeleteLocalRef(path); return ret; } // ---------------------------------------------------------------------------- void JavaBridge::Constructor(JNIEnv* env, jobject obj) { JavaBridge* javaBridge = new JavaBridge(env, obj); env->SetIntField(obj, gJavaBridge_ObjectID, (jint)javaBridge); } void JavaBridge::Finalize(JNIEnv* env, jobject obj) { JavaBridge* javaBridge = (JavaBridge*) (env->GetIntField(obj, gJavaBridge_ObjectID)); ALOG_ASSERT(javaBridge, "Finalize should not be called twice for the same java bridge!"); ALOGV("webcore_javabridge::nativeFinalize(%p)\n", javaBridge); delete javaBridge; env->SetIntField(obj, gJavaBridge_ObjectID, 0); } // we don't use the java bridge object, as we're just looking at a global void JavaBridge::SharedTimerFired(JNIEnv* env, jobject) { if (sSharedTimerFiredCallback) sSharedTimerFiredCallback(); } void JavaBridge::SetCacheSize(JNIEnv* env, jobject obj, jint bytes) { WebCore::memoryCache()->setCapacities(0, bytes/2, bytes); } void JavaBridge::SetNetworkOnLine(JNIEnv* env, jobject obj, jboolean online) { WebCore::networkStateNotifier().networkStateChange(online); } void JavaBridge::SetNetworkType(JNIEnv* env, jobject obj, jstring javatype, jstring javasubtype) { DEFINE_STATIC_LOCAL(AtomicString, wifi, ("wifi")); DEFINE_STATIC_LOCAL(AtomicString, mobile, ("mobile")); DEFINE_STATIC_LOCAL(AtomicString, mobileSupl, ("mobile_supl")); DEFINE_STATIC_LOCAL(AtomicString, gprs, ("gprs")); DEFINE_STATIC_LOCAL(AtomicString, edge, ("edge")); DEFINE_STATIC_LOCAL(AtomicString, umts, ("umts")); String type = jstringToWtfString(env, javatype); String subtype = jstringToWtfString(env, javasubtype); Connection::ConnectionType connectionType = Connection::UNKNOWN; if (type == wifi) connectionType = Connection::WIFI; else if (type == mobile || type == mobileSupl) { if (subtype == edge || subtype == gprs) connectionType = Connection::CELL_2G; else if (subtype == umts) connectionType = Connection::CELL_3G; } WebCore::networkStateNotifier().networkTypeChange(connectionType); } void JavaBridge::ServiceFuncPtrQueue(JNIEnv*) { JavaSharedClient::ServiceFunctionPtrQueue(); } void JavaBridge::UpdatePluginDirectories(JNIEnv* env, jobject obj, jobjectArray array, jboolean reload) { WTF::Vector directories; int count = env->GetArrayLength(array); for (int i = 0; i < count; i++) { jstring dir = (jstring) env->GetObjectArrayElement(array, i); directories.append(jstringToWtfString(env, dir)); env->DeleteLocalRef(dir); } checkException(env); WebCore::PluginDatabase *pluginDatabase = WebCore::PluginDatabase::installedPlugins(); pluginDatabase->setPluginDirectories(directories); // refreshPlugins() should refresh both PluginDatabase and Page's PluginData WebCore::Page::refreshPlugins(reload); } void JavaBridge::AddPackageNames(JNIEnv* env, jobject obj, jobject packageNames) { if (!packageNames) return; // dalvikvm will raise exception if any of these fail jclass setClass = env->FindClass("java/util/Set"); jmethodID iterator = env->GetMethodID(setClass, "iterator", "()Ljava/util/Iterator;"); jobject iter = env->CallObjectMethod(packageNames, iterator); jclass iteratorClass = env->FindClass("java/util/Iterator"); jmethodID hasNext = env->GetMethodID(iteratorClass, "hasNext", "()Z"); jmethodID next = env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;"); HashSet namesSet; while (env->CallBooleanMethod(iter, hasNext)) { jstring name = static_cast(env->CallObjectMethod(iter, next)); namesSet.add(jstringToWtfString(env, name)); env->DeleteLocalRef(name); } packageNotifier().addPackageNames(namesSet); env->DeleteLocalRef(iteratorClass); env->DeleteLocalRef(iter); env->DeleteLocalRef(setClass); } void JavaBridge::AddPackageName(JNIEnv* env, jobject obj, jstring packageName) { packageNotifier().addPackageName(jstringToWtfString(env, packageName)); } void JavaBridge::RemovePackageName(JNIEnv* env, jobject obj, jstring packageName) { packageNotifier().removePackageName(jstringToWtfString(env, packageName)); } void JavaBridge::UpdateProxy(JNIEnv* env, jobject obj, jstring newProxy, jstring newExList) { std::string proxy = jstringToStdString(env, newProxy); std::string exList = jstringToStdString(env, newExList); WebCache::get(false)->proxy()->UpdateProxySettings(proxy, exList); WebCache::get(true)->proxy()->UpdateProxySettings(proxy, exList); } // ---------------------------------------------------------------------------- /* * JNI registration. */ static JNINativeMethod gWebCoreJavaBridgeMethods[] = { /* name, signature, funcPtr */ { "nativeConstructor", "()V", (void*) JavaBridge::Constructor }, { "nativeFinalize", "()V", (void*) JavaBridge::Finalize }, { "sharedTimerFired", "()V", (void*) JavaBridge::SharedTimerFired }, { "setCacheSize", "(I)V", (void*) JavaBridge::SetCacheSize }, { "setNetworkOnLine", "(Z)V", (void*) JavaBridge::SetNetworkOnLine }, { "setNetworkType", "(Ljava/lang/String;Ljava/lang/String;)V", (void*) JavaBridge::SetNetworkType }, { "nativeServiceFuncPtrQueue", "()V", (void*) JavaBridge::ServiceFuncPtrQueue }, { "nativeUpdatePluginDirectories", "([Ljava/lang/String;Z)V", (void*) JavaBridge::UpdatePluginDirectories }, { "addPackageNames", "(Ljava/util/Set;)V", (void*) JavaBridge::AddPackageNames }, { "addPackageName", "(Ljava/lang/String;)V", (void*) JavaBridge::AddPackageName }, { "removePackageName", "(Ljava/lang/String;)V", (void*) JavaBridge::RemovePackageName }, { "nativeUpdateProxy", "(Ljava/lang/String;Ljava/lang/String;)V", (void*) JavaBridge::UpdateProxy } }; int registerJavaBridge(JNIEnv* env) { jclass javaBridge = env->FindClass("android/webkit/JWebCoreJavaBridge"); LOG_FATAL_IF(javaBridge == NULL, "Unable to find class android/webkit/JWebCoreJavaBridge"); gJavaBridge_ObjectID = env->GetFieldID(javaBridge, "mNativeBridge", "I"); LOG_FATAL_IF(gJavaBridge_ObjectID == NULL, "Unable to find android/webkit/JWebCoreJavaBridge.mNativeBridge"); env->DeleteLocalRef(javaBridge); return jniRegisterNativeMethods(env, "android/webkit/JWebCoreJavaBridge", gWebCoreJavaBridgeMethods, NELEM(gWebCoreJavaBridgeMethods)); } } /* namespace android */