summaryrefslogtreecommitdiffstats
path: root/Source/WebKit/android/jni
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebKit/android/jni')
-rw-r--r--Source/WebKit/android/jni/CacheManager.cpp143
-rw-r--r--Source/WebKit/android/jni/CookieManager.cpp201
-rw-r--r--Source/WebKit/android/jni/DeviceMotionAndOrientationManager.cpp171
-rw-r--r--Source/WebKit/android/jni/DeviceMotionAndOrientationManager.h68
-rw-r--r--Source/WebKit/android/jni/DeviceMotionClientImpl.cpp130
-rw-r--r--Source/WebKit/android/jni/DeviceMotionClientImpl.h70
-rw-r--r--Source/WebKit/android/jni/DeviceOrientationClientImpl.cpp130
-rw-r--r--Source/WebKit/android/jni/DeviceOrientationClientImpl.h70
-rwxr-xr-xSource/WebKit/android/jni/GeolocationPermissionsBridge.cpp113
-rw-r--r--Source/WebKit/android/jni/JavaBridge.cpp514
-rw-r--r--Source/WebKit/android/jni/JavaSharedClient.cpp137
-rw-r--r--Source/WebKit/android/jni/JavaSharedClient.h65
-rw-r--r--Source/WebKit/android/jni/JniUtil.cpp58
-rw-r--r--Source/WebKit/android/jni/MIMETypeRegistry.cpp67
-rwxr-xr-xSource/WebKit/android/jni/MockGeolocation.cpp84
-rw-r--r--Source/WebKit/android/jni/PictureSet.cpp676
-rw-r--r--Source/WebKit/android/jni/PictureSet.h104
-rw-r--r--Source/WebKit/android/jni/WebCoreFrameBridge.cpp2125
-rw-r--r--Source/WebKit/android/jni/WebCoreFrameBridge.h173
-rw-r--r--Source/WebKit/android/jni/WebCoreJni.cpp117
-rw-r--r--Source/WebKit/android/jni/WebCoreJni.h96
-rw-r--r--Source/WebKit/android/jni/WebCoreJniOnLoad.cpp319
-rw-r--r--Source/WebKit/android/jni/WebCoreRefObject.h46
-rw-r--r--Source/WebKit/android/jni/WebCoreResourceLoader.cpp352
-rw-r--r--Source/WebKit/android/jni/WebCoreResourceLoader.h78
-rw-r--r--Source/WebKit/android/jni/WebCoreViewBridge.h106
-rw-r--r--Source/WebKit/android/jni/WebFrameView.cpp104
-rw-r--r--Source/WebKit/android/jni/WebFrameView.h65
-rw-r--r--Source/WebKit/android/jni/WebHistory.cpp857
-rw-r--r--Source/WebKit/android/jni/WebHistory.h68
-rw-r--r--Source/WebKit/android/jni/WebIconDatabase.cpp237
-rw-r--r--Source/WebKit/android/jni/WebIconDatabase.h75
-rw-r--r--Source/WebKit/android/jni/WebSettings.cpp599
-rw-r--r--Source/WebKit/android/jni/WebStorage.cpp188
-rw-r--r--Source/WebKit/android/jni/WebViewCore.cpp4598
-rw-r--r--Source/WebKit/android/jni/WebViewCore.h714
36 files changed, 13718 insertions, 0 deletions
diff --git a/Source/WebKit/android/jni/CacheManager.cpp b/Source/WebKit/android/jni/CacheManager.cpp
new file mode 100644
index 0000000..144b62a
--- /dev/null
+++ b/Source/WebKit/android/jni/CacheManager.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2011, 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.
+ */
+
+#include "config.h"
+
+#if USE(CHROME_NETWORK_STACK)
+
+#include "ChromiumIncludes.h"
+#include "WebCache.h"
+#include "WebCoreJni.h"
+
+#include <JNIHelp.h>
+#include <platform/FileSystem.h>
+#include <platform/text/Base64.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/WTFString.h>
+
+using namespace WebCore;
+using namespace base;
+using namespace disk_cache;
+using namespace net;
+using namespace std;
+
+namespace android {
+
+// JNI for android.webkit.CacheManager
+static const char* javaCacheManagerClass = "android/webkit/CacheManager";
+
+static void setStringField(JNIEnv* env, const jobject& object, const jfieldID& field, const String& str)
+{
+ jstring jstr = wtfStringToJstring(env, str);
+ env->SetObjectField(object, field, jstr);
+ env->DeleteLocalRef(jstr);
+}
+
+static void setFieldFromHeaderIfPresent(CacheResult* result, const char* header, JNIEnv* env, const jobject& object, const jfieldID& field, bool allowEmptyString)
+{
+ String value;
+ if (result->firstResponseHeader(header, &value, allowEmptyString))
+ setStringField(env, object, field, value);
+}
+
+static String getCacheFileBaseDir(JNIEnv* env)
+{
+ static String baseDir;
+ if (baseDir.isEmpty()) {
+ jclass cacheManagerClass = env->FindClass("android/webkit/CacheManager");
+ jmethodID getCacheFileBaseDirMethod = env->GetStaticMethodID(cacheManagerClass, "getCacheFileBaseDir", "()Ljava/io/File;");
+ jclass fileClass = env->FindClass("java/io/File");
+ jmethodID getPathMethod = env->GetMethodID(fileClass, "getPath", "()Ljava/lang/String;");
+ jobject fileObject = env->CallStaticObjectMethod(cacheManagerClass, getCacheFileBaseDirMethod);
+ baseDir = jstringToWtfString(env, static_cast<jstring>(env->CallObjectMethod(fileObject, getPathMethod)));
+ }
+ return baseDir;
+}
+
+static jobject getCacheResult(JNIEnv* env, jobject, jstring url)
+{
+ // This is called on the UI thread.
+ scoped_refptr<CacheResult> result = WebCache::get(false /*privateBrowsing*/)->getCacheResult(jstringToWtfString(env, url));
+ if (!result)
+ return 0;
+
+ // We create and populate a file with the cache entry. This allows us to
+ // replicate the behaviour of the Android HTTP stack in the Java
+ // CacheManager, which opens the cache file and provides an input stream to
+ // the file as part of the Java CacheResult object!
+ String urlWtfString = jstringToWtfString(env, url);
+ Vector<char> encodedUrl;
+ base64Encode(urlWtfString.utf8().data(), urlWtfString.length(), encodedUrl, false /*insertLFs*/);
+ String filePath = pathByAppendingComponent(getCacheFileBaseDir(env), encodedUrl.data());
+ if (!result->writeToFile(filePath))
+ return 0;
+
+ jclass cacheResultClass = env->FindClass("android/webkit/CacheManager$CacheResult");
+ jmethodID constructor = env->GetMethodID(cacheResultClass, "<init>", "()V");
+ // We only bother with the fields that are made accessible through the public API.
+ jfieldID contentdispositionField = env->GetFieldID(cacheResultClass, "contentdisposition", "Ljava/lang/String;");
+ jfieldID contentLengthField = env->GetFieldID(cacheResultClass, "contentLength", "J");
+ jfieldID etagField = env->GetFieldID(cacheResultClass, "etag", "Ljava/lang/String;");
+ jfieldID encodingField = env->GetFieldID(cacheResultClass, "encoding", "Ljava/lang/String;");
+ jfieldID expiresField = env->GetFieldID(cacheResultClass, "expires", "J");
+ jfieldID expiresStringField = env->GetFieldID(cacheResultClass, "expiresString", "Ljava/lang/String;");
+ jfieldID httpStatusCodeField = env->GetFieldID(cacheResultClass, "httpStatusCode", "I");
+ jfieldID lastModifiedField = env->GetFieldID(cacheResultClass, "lastModified", "Ljava/lang/String;");
+ jfieldID localPathField = env->GetFieldID(cacheResultClass, "localPath", "Ljava/lang/String;");
+ jfieldID locationField = env->GetFieldID(cacheResultClass, "location", "Ljava/lang/String;");
+ jfieldID mimeTypeField = env->GetFieldID(cacheResultClass, "mimeType", "Ljava/lang/String;");
+
+ jobject javaResult = env->NewObject(cacheResultClass, constructor);
+ setFieldFromHeaderIfPresent(result.get(), "content-disposition", env, javaResult, contentdispositionField, true);
+ env->SetLongField(javaResult, contentLengthField, result->contentSize());
+ setFieldFromHeaderIfPresent(result.get(), "etag", env, javaResult, etagField, false);
+ setStringField(env, javaResult, encodingField, "TODO"); // TODO: Where does the Android stack set this?
+ env->SetLongField(javaResult, expiresField, result->expires());
+ env->SetIntField(javaResult, httpStatusCodeField, result->responseCode());
+ setFieldFromHeaderIfPresent(result.get(), "last-modified", env, javaResult, lastModifiedField, false);
+ setStringField(env, javaResult, localPathField, encodedUrl.data());
+ setFieldFromHeaderIfPresent(result.get(), "location", env, javaResult, locationField, false);
+ setStringField(env, javaResult, mimeTypeField, result->mimeType());
+
+ return javaResult;
+}
+
+static JNINativeMethod gCacheManagerMethods[] = {
+ { "nativeGetCacheResult", "(Ljava/lang/String;)Landroid/webkit/CacheManager$CacheResult;", (void*) getCacheResult },
+};
+
+int registerCacheManager(JNIEnv* env)
+{
+#ifndef NDEBUG
+ jclass cacheManager = env->FindClass(javaCacheManagerClass);
+ LOG_ASSERT(cacheManager, "Unable to find class");
+ env->DeleteLocalRef(cacheManager);
+#endif
+ return jniRegisterNativeMethods(env, javaCacheManagerClass, gCacheManagerMethods, NELEM(gCacheManagerMethods));
+}
+
+} // namespace android
+
+#endif // USE(CHROME_NETWORK_STACK)
diff --git a/Source/WebKit/android/jni/CookieManager.cpp b/Source/WebKit/android/jni/CookieManager.cpp
new file mode 100644
index 0000000..0bdf303
--- /dev/null
+++ b/Source/WebKit/android/jni/CookieManager.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2010, 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.
+ */
+
+#include "config.h"
+
+#include "ChromiumIncludes.h"
+#include "WebCookieJar.h"
+#include "WebCoreJni.h"
+#include <JNIHelp.h>
+
+using namespace base;
+using namespace net;
+
+namespace android {
+
+// JNI for android.webkit.CookieManager
+static const char* javaCookieManagerClass = "android/webkit/CookieManager";
+
+static bool acceptCookie(JNIEnv*, jobject)
+{
+#if USE(CHROME_NETWORK_STACK)
+ // This is a static method which gets the cookie policy for all WebViews. We
+ // always apply the same configuration to the contexts for both regular and
+ // private browsing, so expect the same result here.
+ bool regularAcceptCookies = WebCookieJar::get(false)->allowCookies();
+ ASSERT(regularAcceptCookies == WebCookieJar::get(true)->allowCookies());
+ return regularAcceptCookies;
+#else
+ // The Android HTTP stack is implemented Java-side.
+ ASSERT_NOT_REACHED();
+ return false;
+#endif
+}
+
+static jstring getCookie(JNIEnv* env, jobject, jstring url, jboolean privateBrowsing)
+{
+#if USE(CHROME_NETWORK_STACK)
+ GURL gurl(jstringToStdString(env, url));
+ CookieOptions options;
+ options.set_include_httponly();
+ std::string cookies = WebCookieJar::get(privateBrowsing)->cookieStore()->GetCookieMonster()->GetCookiesWithOptions(gurl, options);
+ return stdStringToJstring(env, cookies);
+#else
+ // The Android HTTP stack is implemented Java-side.
+ ASSERT_NOT_REACHED();
+ return jstring();
+#endif
+}
+
+static bool hasCookies(JNIEnv*, jobject, jboolean privateBrowsing)
+{
+#if USE(CHROME_NETWORK_STACK)
+ return WebCookieJar::get(privateBrowsing)->getNumCookiesInDatabase() > 0;
+#else
+ // The Android HTTP stack is implemented Java-side.
+ ASSERT_NOT_REACHED();
+ return false;
+#endif
+}
+
+static void removeAllCookie(JNIEnv*, jobject)
+{
+#if USE(CHROME_NETWORK_STACK)
+ WebCookieJar::get(false)->cookieStore()->GetCookieMonster()->DeleteAll(true);
+ // This will lazily create a new private browsing context. However, if the
+ // context doesn't already exist, there's no need to create it, as cookies
+ // for such contexts are cleared up when we're done with them.
+ // TODO: Consider adding an optimisation to not create the context if it
+ // doesn't already exist.
+ WebCookieJar::get(true)->cookieStore()->GetCookieMonster()->DeleteAll(true);
+
+ // The Java code removes cookies directly from the backing database, so we do the same,
+ // but with a NULL callback so it's asynchronous.
+ WebCookieJar::get(true)->cookieStore()->GetCookieMonster()->FlushStore(NULL);
+#endif
+}
+
+static void removeExpiredCookie(JNIEnv*, jobject)
+{
+#if USE(CHROME_NETWORK_STACK)
+ // This simply forces a GC. The getters delete expired cookies so won't return expired cookies anyway.
+ WebCookieJar::get(false)->cookieStore()->GetCookieMonster()->GetAllCookies();
+ WebCookieJar::get(true)->cookieStore()->GetCookieMonster()->GetAllCookies();
+#endif
+}
+
+static void removeSessionCookies(WebCookieJar* cookieJar)
+{
+#if USE(CHROME_NETWORK_STACK)
+ CookieMonster* cookieMonster = cookieJar->cookieStore()->GetCookieMonster();
+ CookieMonster::CookieList cookies = cookieMonster->GetAllCookies();
+ for (CookieMonster::CookieList::const_iterator iter = cookies.begin(); iter != cookies.end(); ++iter) {
+ if (iter->IsSessionCookie())
+ cookieMonster->DeleteCanonicalCookie(*iter);
+ }
+#endif
+}
+
+static void removeSessionCookie(JNIEnv*, jobject)
+{
+#if USE(CHROME_NETWORK_STACK)
+ removeSessionCookies(WebCookieJar::get(false));
+ removeSessionCookies(WebCookieJar::get(true));
+#endif
+}
+
+static void setAcceptCookie(JNIEnv*, jobject, jboolean accept)
+{
+#if USE(CHROME_NETWORK_STACK)
+ // This is a static method which configures the cookie policy for all
+ // WebViews, so we configure the contexts for both regular and private
+ // browsing.
+ WebCookieJar::get(false)->setAllowCookies(accept);
+ WebCookieJar::get(true)->setAllowCookies(accept);
+#endif
+}
+
+static void setCookie(JNIEnv* env, jobject, jstring url, jstring value, jboolean privateBrowsing)
+{
+#if USE(CHROME_NETWORK_STACK)
+ GURL gurl(jstringToStdString(env, url));
+ std::string line(jstringToStdString(env, value));
+ CookieOptions options;
+ options.set_include_httponly();
+ WebCookieJar::get(privateBrowsing)->cookieStore()->GetCookieMonster()->SetCookieWithOptions(gurl, line, options);
+#endif
+}
+
+static void flushCookieStore(JNIEnv*, jobject)
+{
+#if USE(CHROME_NETWORK_STACK)
+ WebCookieJar::flush();
+#endif
+}
+
+static bool acceptFileSchemeCookies(JNIEnv*, jobject)
+{
+#if USE(CHROME_NETWORK_STACK)
+ return WebCookieJar::acceptFileSchemeCookies();
+#else
+ // File scheme cookies are always accepted with the Android HTTP stack.
+ return true;
+#endif
+}
+
+static void setAcceptFileSchemeCookies(JNIEnv*, jobject, jboolean accept)
+{
+#if USE(CHROME_NETWORK_STACK)
+ WebCookieJar::setAcceptFileSchemeCookies(accept);
+#else
+ // File scheme cookies are always accepted with the Android HTTP stack.
+#endif
+}
+
+static JNINativeMethod gCookieManagerMethods[] = {
+ { "nativeAcceptCookie", "()Z", (void*) acceptCookie },
+ { "nativeGetCookie", "(Ljava/lang/String;Z)Ljava/lang/String;", (void*) getCookie },
+ { "nativeHasCookies", "(Z)Z", (void*) hasCookies },
+ { "nativeRemoveAllCookie", "()V", (void*) removeAllCookie },
+ { "nativeRemoveExpiredCookie", "()V", (void*) removeExpiredCookie },
+ { "nativeRemoveSessionCookie", "()V", (void*) removeSessionCookie },
+ { "nativeSetAcceptCookie", "(Z)V", (void*) setAcceptCookie },
+ { "nativeSetCookie", "(Ljava/lang/String;Ljava/lang/String;Z)V", (void*) setCookie },
+ { "nativeFlushCookieStore", "()V", (void*) flushCookieStore },
+ { "nativeAcceptFileSchemeCookies", "()Z", (void*) acceptFileSchemeCookies },
+ { "nativeSetAcceptFileSchemeCookies", "(Z)V", (void*) setAcceptFileSchemeCookies },
+};
+
+int registerCookieManager(JNIEnv* env)
+{
+#ifndef NDEBUG
+ jclass cookieManager = env->FindClass(javaCookieManagerClass);
+ LOG_ASSERT(cookieManager, "Unable to find class");
+ env->DeleteLocalRef(cookieManager);
+#endif
+ return jniRegisterNativeMethods(env, javaCookieManagerClass, gCookieManagerMethods, NELEM(gCookieManagerMethods));
+}
+
+} // namespace android
diff --git a/Source/WebKit/android/jni/DeviceMotionAndOrientationManager.cpp b/Source/WebKit/android/jni/DeviceMotionAndOrientationManager.cpp
new file mode 100644
index 0000000..8beb372
--- /dev/null
+++ b/Source/WebKit/android/jni/DeviceMotionAndOrientationManager.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2010, 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.
+ */
+
+#include "config.h"
+#include "DeviceMotionAndOrientationManager.h"
+
+#include "DeviceMotionClientImpl.h"
+#include "DeviceOrientationClientImpl.h"
+#include "DeviceOrientationController.h"
+#include "WebViewCore.h"
+#include "Frame.h"
+#include "Page.h"
+
+#include <DeviceOrientationClientMock.h>
+#include <JNIHelp.h>
+
+using namespace WebCore;
+
+namespace android {
+
+DeviceMotionAndOrientationManager::DeviceMotionAndOrientationManager(WebViewCore* webViewCore)
+ : m_useMock(false)
+ , m_webViewCore(webViewCore)
+{
+}
+
+void DeviceMotionAndOrientationManager::useMock()
+{
+ m_useMock = true;
+}
+
+void DeviceMotionAndOrientationManager::setMockMotion(PassRefPtr<DeviceMotionData> motion)
+{
+ // TODO: There is not yet a DeviceMotion mock.
+}
+
+void DeviceMotionAndOrientationManager::onMotionChange(PassRefPtr<DeviceMotionData> motion)
+{
+ ASSERT(!m_useMock);
+ static_cast<DeviceMotionClientImpl*>(m_motionClient.get())->onMotionChange(motion);
+}
+
+void DeviceMotionAndOrientationManager::setMockOrientation(PassRefPtr<DeviceOrientation> orientation)
+{
+ if (m_useMock)
+ static_cast<DeviceOrientationClientMock*>(orientationClient())->setOrientation(orientation);
+}
+
+void DeviceMotionAndOrientationManager::onOrientationChange(PassRefPtr<DeviceOrientation> orientation)
+{
+ ASSERT(!m_useMock);
+ static_cast<DeviceOrientationClientImpl*>(m_orientationClient.get())->onOrientationChange(orientation);
+}
+
+void DeviceMotionAndOrientationManager::maybeSuspendClients()
+{
+ if (!m_useMock) {
+ if (m_motionClient)
+ static_cast<DeviceMotionClientImpl*>(m_motionClient.get())->suspend();
+ if (m_orientationClient)
+ static_cast<DeviceOrientationClientImpl*>(m_orientationClient.get())->suspend();
+ }
+}
+
+void DeviceMotionAndOrientationManager::maybeResumeClients()
+{
+ if (!m_useMock) {
+ if (m_motionClient)
+ static_cast<DeviceMotionClientImpl*>(m_motionClient.get())->resume();
+ if (m_orientationClient)
+ static_cast<DeviceOrientationClientImpl*>(m_orientationClient.get())->resume();
+ }
+}
+
+DeviceMotionClient* DeviceMotionAndOrientationManager::motionClient()
+{
+ // TODO: There is not yet a DeviceMotion mock.
+ if (!m_motionClient)
+ m_motionClient.set(m_useMock ? 0
+ : static_cast<DeviceMotionClient*>(new DeviceMotionClientImpl(m_webViewCore)));
+ ASSERT(m_motionClient);
+ return m_motionClient.get();
+}
+
+DeviceOrientationClient* DeviceMotionAndOrientationManager::orientationClient()
+{
+ if (!m_orientationClient)
+ m_orientationClient.set(m_useMock ? new DeviceOrientationClientMock
+ : static_cast<DeviceOrientationClient*>(new DeviceOrientationClientImpl(m_webViewCore)));
+ ASSERT(m_orientationClient);
+ return m_orientationClient.get();
+}
+
+// JNI for android.webkit.DeviceMotionAndOrientationManager
+static const char* javaDeviceMotionAndOrientationManagerClass = "android/webkit/DeviceMotionAndOrientationManager";
+
+static WebViewCore* getWebViewCore(JNIEnv* env, jobject webViewCoreObject)
+{
+ jclass webViewCoreClass = env->FindClass("android/webkit/WebViewCore");
+ jfieldID nativeClassField = env->GetFieldID(webViewCoreClass, "mNativeClass", "I");
+ env->DeleteLocalRef(webViewCoreClass);
+ return reinterpret_cast<WebViewCore*>(env->GetIntField(webViewCoreObject, nativeClassField));
+}
+
+static void useMock(JNIEnv* env, jobject, jobject webViewCoreObject)
+{
+ getWebViewCore(env, webViewCoreObject)->deviceMotionAndOrientationManager()->useMock();
+}
+
+static void onMotionChange(JNIEnv* env, jobject, jobject webViewCoreObject, bool canProvideX, double x, bool canProvideY, double y, bool canProvideZ, double z, double interval)
+{
+ // We only provide accelerationIncludingGravity.
+ RefPtr<DeviceMotionData::Acceleration> accelerationIncludingGravity = DeviceMotionData::Acceleration::create(canProvideX, x, canProvideY, y, canProvideZ, z);
+ bool canProvideInterval = canProvideX || canProvideY || canProvideZ;
+ RefPtr<DeviceMotionData> motion = DeviceMotionData::create(0, accelerationIncludingGravity.release(), 0, canProvideInterval, interval);
+ getWebViewCore(env, webViewCoreObject)->deviceMotionAndOrientationManager()->onMotionChange(motion.release());
+}
+
+static void setMockOrientation(JNIEnv* env, jobject, jobject webViewCoreObject, bool canProvideAlpha, double alpha, bool canProvideBeta, double beta, bool canProvideGamma, double gamma)
+{
+ RefPtr<DeviceOrientation> orientation = DeviceOrientation::create(canProvideAlpha, alpha, canProvideBeta, beta, canProvideGamma, gamma);
+ getWebViewCore(env, webViewCoreObject)->deviceMotionAndOrientationManager()->setMockOrientation(orientation.release());
+}
+
+static void onOrientationChange(JNIEnv* env, jobject, jobject webViewCoreObject, bool canProvideAlpha, double alpha, bool canProvideBeta, double beta, bool canProvideGamma, double gamma)
+{
+ RefPtr<DeviceOrientation> orientation = DeviceOrientation::create(canProvideAlpha, alpha, canProvideBeta, beta, canProvideGamma, gamma);
+ getWebViewCore(env, webViewCoreObject)->deviceMotionAndOrientationManager()->onOrientationChange(orientation.release());
+}
+
+static JNINativeMethod gDeviceMotionAndOrientationManagerMethods[] = {
+ { "nativeUseMock", "(Landroid/webkit/WebViewCore;)V", (void*) useMock },
+ { "nativeOnMotionChange", "(Landroid/webkit/WebViewCore;ZDZDZDD)V", (void*) onMotionChange },
+ { "nativeSetMockOrientation", "(Landroid/webkit/WebViewCore;ZDZDZD)V", (void*) setMockOrientation },
+ { "nativeOnOrientationChange", "(Landroid/webkit/WebViewCore;ZDZDZD)V", (void*) onOrientationChange }
+};
+
+int registerDeviceMotionAndOrientationManager(JNIEnv* env)
+{
+#ifndef NDEBUG
+ jclass deviceMotionAndOrientationManager = env->FindClass(javaDeviceMotionAndOrientationManagerClass);
+ LOG_ASSERT(deviceMotionAndOrientationManager, "Unable to find class");
+ env->DeleteLocalRef(deviceMotionAndOrientationManager);
+#endif
+
+ return jniRegisterNativeMethods(env, javaDeviceMotionAndOrientationManagerClass, gDeviceMotionAndOrientationManagerMethods, NELEM(gDeviceMotionAndOrientationManagerMethods));
+}
+
+} // namespace android
diff --git a/Source/WebKit/android/jni/DeviceMotionAndOrientationManager.h b/Source/WebKit/android/jni/DeviceMotionAndOrientationManager.h
new file mode 100644
index 0000000..44463c1
--- /dev/null
+++ b/Source/WebKit/android/jni/DeviceMotionAndOrientationManager.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2010, 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.
+ */
+
+#ifndef DeviceMotionAndOrientationManager_h
+#define DeviceMotionAndOrientationManager_h
+
+#include <DeviceMotionData.h>
+#include <DeviceMotionClient.h>
+#include <DeviceOrientation.h>
+#include <DeviceOrientationClient.h>
+#include <OwnPtr.h>
+#include <PassRefPtr.h>
+#include <RefPtr.h>
+
+namespace android {
+
+class WebViewCore;
+
+// This class takes care of the fact that the clients used for DeviceMotion and
+// DeviceOrientation may be either the real implementations or mocks. It also
+// handles setting the data on both the real and mock clients. This class is
+// owned by WebViewCore and exists to keep cruft out of that class.
+class DeviceMotionAndOrientationManager {
+public:
+ DeviceMotionAndOrientationManager(WebViewCore*);
+
+ void useMock();
+ void setMockMotion(PassRefPtr<WebCore::DeviceMotionData>);
+ void onMotionChange(PassRefPtr<WebCore::DeviceMotionData>);
+ void setMockOrientation(PassRefPtr<WebCore::DeviceOrientation>);
+ void onOrientationChange(PassRefPtr<WebCore::DeviceOrientation>);
+ void maybeSuspendClients();
+ void maybeResumeClients();
+ WebCore::DeviceMotionClient* motionClient();
+ WebCore::DeviceOrientationClient* orientationClient();
+
+private:
+ bool m_useMock;
+ WebViewCore* m_webViewCore;
+ OwnPtr<WebCore::DeviceMotionClient> m_motionClient;
+ OwnPtr<WebCore::DeviceOrientationClient> m_orientationClient;
+};
+
+} // namespace android
+
+#endif // DeviceMotionAndOrientationManager_h
diff --git a/Source/WebKit/android/jni/DeviceMotionClientImpl.cpp b/Source/WebKit/android/jni/DeviceMotionClientImpl.cpp
new file mode 100644
index 0000000..82f3c35
--- /dev/null
+++ b/Source/WebKit/android/jni/DeviceMotionClientImpl.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2010, 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.
+ */
+
+#include "config.h"
+#include "DeviceMotionClientImpl.h"
+
+#include "WebViewCore.h"
+#include <DeviceMotionController.h>
+#include <Frame.h>
+#include <JNIHelp.h>
+
+namespace android {
+
+using JSC::Bindings::getJNIEnv;
+
+enum javaServiceClassMethods {
+ ServiceMethodStart = 0,
+ ServiceMethodStop,
+ ServiceMethodSuspend,
+ ServiceMethodResume,
+ ServiceMethodCount
+};
+static jmethodID javaServiceClassMethodIDs[ServiceMethodCount];
+
+DeviceMotionClientImpl::DeviceMotionClientImpl(WebViewCore* webViewCore)
+ : m_webViewCore(webViewCore)
+ , m_javaServiceObject(0)
+{
+ ASSERT(m_webViewCore);
+}
+
+DeviceMotionClientImpl::~DeviceMotionClientImpl()
+{
+ releaseJavaInstance();
+}
+
+jobject DeviceMotionClientImpl::getJavaInstance()
+{
+ // Lazily get the Java object. We can't do this until the WebViewCore is all
+ // set up.
+ if (m_javaServiceObject)
+ return m_javaServiceObject;
+
+ JNIEnv* env = getJNIEnv();
+
+ ASSERT(m_webViewCore);
+ jobject object = m_webViewCore->getDeviceMotionService();
+
+ // Get the Java DeviceMotionService class.
+ jclass javaServiceClass = env->GetObjectClass(object);
+ ASSERT(javaServiceClass);
+
+ // Set up the methods we wish to call on the Java DeviceMotionService
+ // class.
+ javaServiceClassMethodIDs[ServiceMethodStart] =
+ env->GetMethodID(javaServiceClass, "start", "()V");
+ javaServiceClassMethodIDs[ServiceMethodStop] =
+ env->GetMethodID(javaServiceClass, "stop", "()V");
+ javaServiceClassMethodIDs[ServiceMethodSuspend] =
+ env->GetMethodID(javaServiceClass, "suspend", "()V");
+ javaServiceClassMethodIDs[ServiceMethodResume] =
+ env->GetMethodID(javaServiceClass, "resume", "()V");
+ env->DeleteLocalRef(javaServiceClass);
+
+ m_javaServiceObject = getJNIEnv()->NewGlobalRef(object);
+ getJNIEnv()->DeleteLocalRef(object);
+
+ ASSERT(m_javaServiceObject);
+ return m_javaServiceObject;
+}
+
+void DeviceMotionClientImpl::releaseJavaInstance()
+{
+ ASSERT(m_javaServiceObject);
+ getJNIEnv()->DeleteGlobalRef(m_javaServiceObject);
+}
+
+void DeviceMotionClientImpl::startUpdating()
+{
+ getJNIEnv()->CallVoidMethod(getJavaInstance(),
+ javaServiceClassMethodIDs[ServiceMethodStart]);
+}
+
+void DeviceMotionClientImpl::stopUpdating()
+{
+ getJNIEnv()->CallVoidMethod(getJavaInstance(),
+ javaServiceClassMethodIDs[ServiceMethodStop]);
+}
+
+void DeviceMotionClientImpl::onMotionChange(PassRefPtr<DeviceMotionData> motion)
+{
+ m_lastMotion = motion;
+ m_controller->didChangeDeviceMotion(m_lastMotion.get());
+}
+
+void DeviceMotionClientImpl::suspend()
+{
+ getJNIEnv()->CallVoidMethod(getJavaInstance(),
+ javaServiceClassMethodIDs[ServiceMethodSuspend]);
+}
+
+void DeviceMotionClientImpl::resume()
+{
+ getJNIEnv()->CallVoidMethod(getJavaInstance(),
+ javaServiceClassMethodIDs[ServiceMethodResume]);
+}
+
+} // namespace android
diff --git a/Source/WebKit/android/jni/DeviceMotionClientImpl.h b/Source/WebKit/android/jni/DeviceMotionClientImpl.h
new file mode 100644
index 0000000..c979098
--- /dev/null
+++ b/Source/WebKit/android/jni/DeviceMotionClientImpl.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2010, 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.
+ */
+
+#ifndef DeviceMotionClientImpl_h
+#define DeviceMotionClientImpl_h
+
+#include <DeviceMotionClient.h>
+#include <DeviceMotionData.h>
+#include <JNIUtility.h>
+#include <PassRefPtr.h>
+#include <RefPtr.h>
+
+using namespace WebCore;
+
+namespace android {
+
+class DeviceMotionAndOrientationManager;
+class WebViewCore;
+
+class DeviceMotionClientImpl : public DeviceMotionClient {
+public:
+ DeviceMotionClientImpl(WebViewCore*);
+ virtual ~DeviceMotionClientImpl();
+
+ void onMotionChange(PassRefPtr<DeviceMotionData>);
+ void suspend();
+ void resume();
+
+ // DeviceMotionClient methods
+ virtual void startUpdating();
+ virtual void stopUpdating();
+ virtual DeviceMotionData* currentDeviceMotion() const { return m_lastMotion.get(); }
+ virtual void setController(DeviceMotionController* controller) { m_controller = controller; }
+ virtual void deviceMotionControllerDestroyed() { }
+
+private:
+ jobject getJavaInstance();
+ void releaseJavaInstance();
+
+ WebViewCore* m_webViewCore;
+ jobject m_javaServiceObject;
+ DeviceMotionController* m_controller;
+ RefPtr<DeviceMotionData> m_lastMotion;
+};
+
+} // namespace android
+
+#endif // DeviceMotionClientImpl_h
diff --git a/Source/WebKit/android/jni/DeviceOrientationClientImpl.cpp b/Source/WebKit/android/jni/DeviceOrientationClientImpl.cpp
new file mode 100644
index 0000000..bf3b3c3
--- /dev/null
+++ b/Source/WebKit/android/jni/DeviceOrientationClientImpl.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2010, 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.
+ */
+
+#include "config.h"
+#include "DeviceOrientationClientImpl.h"
+
+#include "WebViewCore.h"
+#include <DeviceOrientationController.h>
+#include <Frame.h>
+#include <JNIHelp.h>
+
+namespace android {
+
+using JSC::Bindings::getJNIEnv;
+
+enum javaDeviceOrientationServiceClassMethods {
+ DeviceOrientationServiceMethodStart = 0,
+ DeviceOrientationServiceMethodStop,
+ DeviceOrientationServiceMethodSuspend,
+ DeviceOrientationServiceMethodResume,
+ DeviceOrientationServiceMethodCount
+};
+static jmethodID javaDeviceOrientationServiceClassMethodIDs[DeviceOrientationServiceMethodCount];
+
+DeviceOrientationClientImpl::DeviceOrientationClientImpl(WebViewCore* webViewCore)
+ : m_webViewCore(webViewCore)
+ , m_javaDeviceOrientationServiceObject(0)
+{
+ ASSERT(m_webViewCore);
+}
+
+DeviceOrientationClientImpl::~DeviceOrientationClientImpl()
+{
+ releaseJavaInstance();
+}
+
+jobject DeviceOrientationClientImpl::getJavaInstance()
+{
+ // Lazily get the Java object. We can't do this until the WebViewCore is all
+ // set up.
+ if (m_javaDeviceOrientationServiceObject)
+ return m_javaDeviceOrientationServiceObject;
+
+ JNIEnv* env = getJNIEnv();
+
+ ASSERT(m_webViewCore);
+ jobject object = m_webViewCore->getDeviceOrientationService();
+
+ // Get the Java DeviceOrientationService class.
+ jclass javaDeviceOrientationServiceClass = env->GetObjectClass(object);
+ ASSERT(javaDeviceOrientationServiceClass);
+
+ // Set up the methods we wish to call on the Java DeviceOrientationService
+ // class.
+ javaDeviceOrientationServiceClassMethodIDs[DeviceOrientationServiceMethodStart] =
+ env->GetMethodID(javaDeviceOrientationServiceClass, "start", "()V");
+ javaDeviceOrientationServiceClassMethodIDs[DeviceOrientationServiceMethodStop] =
+ env->GetMethodID(javaDeviceOrientationServiceClass, "stop", "()V");
+ javaDeviceOrientationServiceClassMethodIDs[DeviceOrientationServiceMethodSuspend] =
+ env->GetMethodID(javaDeviceOrientationServiceClass, "suspend", "()V");
+ javaDeviceOrientationServiceClassMethodIDs[DeviceOrientationServiceMethodResume] =
+ env->GetMethodID(javaDeviceOrientationServiceClass, "resume", "()V");
+ env->DeleteLocalRef(javaDeviceOrientationServiceClass);
+
+ m_javaDeviceOrientationServiceObject = getJNIEnv()->NewGlobalRef(object);
+ getJNIEnv()->DeleteLocalRef(object);
+
+ ASSERT(m_javaDeviceOrientationServiceObject);
+ return m_javaDeviceOrientationServiceObject;
+}
+
+void DeviceOrientationClientImpl::releaseJavaInstance()
+{
+ ASSERT(m_javaDeviceOrientationServiceObject);
+ getJNIEnv()->DeleteGlobalRef(m_javaDeviceOrientationServiceObject);
+}
+
+void DeviceOrientationClientImpl::startUpdating()
+{
+ getJNIEnv()->CallVoidMethod(getJavaInstance(),
+ javaDeviceOrientationServiceClassMethodIDs[DeviceOrientationServiceMethodStart]);
+}
+
+void DeviceOrientationClientImpl::stopUpdating()
+{
+ getJNIEnv()->CallVoidMethod(getJavaInstance(),
+ javaDeviceOrientationServiceClassMethodIDs[DeviceOrientationServiceMethodStop]);
+}
+
+void DeviceOrientationClientImpl::onOrientationChange(PassRefPtr<DeviceOrientation> orientation)
+{
+ m_lastOrientation = orientation;
+ m_controller->didChangeDeviceOrientation(m_lastOrientation.get());
+}
+
+void DeviceOrientationClientImpl::suspend()
+{
+ getJNIEnv()->CallVoidMethod(getJavaInstance(),
+ javaDeviceOrientationServiceClassMethodIDs[DeviceOrientationServiceMethodSuspend]);
+}
+
+void DeviceOrientationClientImpl::resume()
+{
+ getJNIEnv()->CallVoidMethod(getJavaInstance(),
+ javaDeviceOrientationServiceClassMethodIDs[DeviceOrientationServiceMethodResume]);
+}
+
+} // namespace android
diff --git a/Source/WebKit/android/jni/DeviceOrientationClientImpl.h b/Source/WebKit/android/jni/DeviceOrientationClientImpl.h
new file mode 100644
index 0000000..0e3f6b3
--- /dev/null
+++ b/Source/WebKit/android/jni/DeviceOrientationClientImpl.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2010, 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.
+ */
+
+#ifndef DeviceOrientationClientImpl_h
+#define DeviceOrientationClientImpl_h
+
+#include <DeviceOrientation.h>
+#include <DeviceOrientationClient.h>
+#include <JNIUtility.h>
+#include <PassRefPtr.h>
+#include <RefPtr.h>
+
+using namespace WebCore;
+
+namespace android {
+
+class DeviceMotionAndOrientationManager;
+class WebViewCore;
+
+class DeviceOrientationClientImpl : public DeviceOrientationClient {
+public:
+ DeviceOrientationClientImpl(WebViewCore*);
+ virtual ~DeviceOrientationClientImpl();
+
+ void onOrientationChange(PassRefPtr<DeviceOrientation>);
+ void suspend();
+ void resume();
+
+ // DeviceOrientationClient methods
+ virtual void startUpdating();
+ virtual void stopUpdating();
+ virtual DeviceOrientation* lastOrientation() const { return m_lastOrientation.get(); }
+ virtual void setController(DeviceOrientationController* controller) { m_controller = controller; }
+ virtual void deviceOrientationControllerDestroyed() { }
+
+private:
+ jobject getJavaInstance();
+ void releaseJavaInstance();
+
+ WebViewCore* m_webViewCore;
+ jobject m_javaDeviceOrientationServiceObject;
+ DeviceOrientationController* m_controller;
+ RefPtr<DeviceOrientation> m_lastOrientation;
+};
+
+} // namespace android
+
+#endif // DeviceOrientationClientImpl_h
diff --git a/Source/WebKit/android/jni/GeolocationPermissionsBridge.cpp b/Source/WebKit/android/jni/GeolocationPermissionsBridge.cpp
new file mode 100755
index 0000000..a366601
--- /dev/null
+++ b/Source/WebKit/android/jni/GeolocationPermissionsBridge.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2009, 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.
+ */
+
+#include "config.h"
+
+#include <JNIHelp.h> // For jniRegisterNativeMethods
+#include "GeolocationPermissions.h"
+#include "WebCoreJni.h" // For jstringToWtfString
+
+
+/**
+ * This file provides a set of functions to bridge between the Java and C++
+ * GeolocationPermissions classes. The java GeolocationPermissions object calls
+ * the functions provided here, which in turn call static methods on the C++
+ * GeolocationPermissions class.
+ */
+
+namespace android {
+
+static jobject getOrigins(JNIEnv* env, jobject obj)
+{
+ GeolocationPermissions::OriginSet origins = GeolocationPermissions::getOrigins();
+
+ jclass setClass = env->FindClass("java/util/HashSet");
+ jmethodID constructor = env->GetMethodID(setClass, "<init>", "()V");
+ jmethodID addMethod = env->GetMethodID(setClass, "add", "(Ljava/lang/Object;)Z");
+ jobject set = env->NewObject(setClass, constructor);
+ env->DeleteLocalRef(setClass);
+
+ GeolocationPermissions::OriginSet::const_iterator end = origins.end();
+ for (GeolocationPermissions::OriginSet::const_iterator iter = origins.begin(); iter != end; ++iter) {
+ jstring originString = wtfStringToJstring(env, *iter);
+ env->CallBooleanMethod(set, addMethod, originString);
+ env->DeleteLocalRef(originString);
+ }
+ return set;
+}
+
+static bool getAllowed(JNIEnv* env, jobject obj, jstring origin)
+{
+ WTF::String originString = jstringToWtfString(env, origin);
+ return GeolocationPermissions::getAllowed(originString);
+}
+
+static void clear(JNIEnv* env, jobject obj, jstring origin)
+{
+ WTF::String originString = jstringToWtfString(env, origin);
+ GeolocationPermissions::clear(originString);
+}
+
+static void allow(JNIEnv* env, jobject obj, jstring origin)
+{
+ WTF::String originString = jstringToWtfString(env, origin);
+ GeolocationPermissions::allow(originString);
+}
+
+static void clearAll(JNIEnv* env, jobject obj)
+{
+ GeolocationPermissions::clearAll();
+}
+
+/*
+ * JNI registration
+ */
+static JNINativeMethod gGeolocationPermissionsMethods[] = {
+ { "nativeGetOrigins", "()Ljava/util/Set;",
+ (void*) getOrigins },
+ { "nativeGetAllowed", "(Ljava/lang/String;)Z",
+ (void*) getAllowed },
+ { "nativeClear", "(Ljava/lang/String;)V",
+ (void*) clear },
+ { "nativeAllow", "(Ljava/lang/String;)V",
+ (void*) allow },
+ { "nativeClearAll", "()V",
+ (void*) clearAll }
+};
+
+int registerGeolocationPermissions(JNIEnv* env)
+{
+ const char* kGeolocationPermissionsClass = "android/webkit/GeolocationPermissions";
+#ifndef NDEBUG
+ jclass geolocationPermissions = env->FindClass(kGeolocationPermissionsClass);
+ LOG_ASSERT(geolocationPermissions, "Unable to find class");
+ env->DeleteLocalRef(geolocationPermissions);
+#endif
+
+ return jniRegisterNativeMethods(env, kGeolocationPermissionsClass,
+ gGeolocationPermissionsMethods, NELEM(gGeolocationPermissionsMethods));
+}
+
+}
diff --git a/Source/WebKit/android/jni/JavaBridge.cpp b/Source/WebKit/android/jni/JavaBridge.cpp
new file mode 100644
index 0000000..2fa12fc
--- /dev/null
+++ b/Source/WebKit/android/jni/JavaBridge.cpp
@@ -0,0 +1,514 @@
+/*
+ * 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"
+#ifdef ANDROID_INSTRUMENT
+#include "TimeCounter.h"
+#endif
+#include "WebCache.h"
+#include "WebCoreJni.h"
+
+#include <JNIHelp.h>
+#include <JNIUtility.h>
+#include <SkUtils.h>
+#include <jni.h>
+#include <utils/misc.h>
+#include <wtf/Platform.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/text/AtomicString.h>
+
+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<WTF::String> getPluginDirectories();
+ virtual WTF::String getPluginSharedDataDirectory();
+
+ virtual WTF::Vector<String> 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);
+
+
+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);
+
+ LOG_ASSERT(mSetSharedTimer, "Could not find method setSharedTimer");
+ LOG_ASSERT(mStopSharedTimer, "Could not find method stopSharedTimer");
+ LOG_ASSERT(mSetCookies, "Could not find method setCookies");
+ LOG_ASSERT(mCookies, "Could not find method cookies");
+ LOG_ASSERT(mCookiesEnabled, "Could not find method cookiesEnabled");
+ LOG_ASSERT(mGetPluginDirectories, "Could not find method getPluginDirectories");
+ LOG_ASSERT(mGetPluginSharedDataDirectory, "Could not find method getPluginSharedDataDirectory");
+ LOG_ASSERT(mGetKeyStrengthList, "Could not find method getKeyStrengthList");
+ LOG_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);
+ env->CallVoidMethod(obj.get(), mSetSharedTimer, timemillis);
+}
+
+void
+JavaBridge::stopSharedTimer()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject obj = javaObject(env);
+ env->CallVoidMethod(obj.get(), mStopSharedTimer);
+}
+
+void
+JavaBridge::setCookies(WebCore::KURL const& url, WTF::String const& value)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ const WTF::String& urlStr = url.string();
+ jstring jUrlStr = wtfStringToJstring(env, urlStr);
+ jstring jValueStr = wtfStringToJstring(env, value);
+
+ AutoJObject obj = javaObject(env);
+ 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();
+ const WTF::String& urlStr = url.string();
+ jstring jUrlStr = wtfStringToJstring(env, urlStr);
+
+ AutoJObject obj = javaObject(env);
+ 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);
+ jboolean ret = env->CallBooleanMethod(obj.get(), mCookiesEnabled);
+ return (ret != 0);
+}
+
+WTF::Vector<WTF::String>
+JavaBridge::getPluginDirectories()
+{
+ WTF::Vector<WTF::String> directories;
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject obj = javaObject(env);
+ 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);
+ jstring ret = (jstring)env->CallObjectMethod(obj.get(), mGetPluginSharedDataDirectory);
+ WTF::String path = jstringToWtfString(env, ret);
+ checkException(env);
+ return path;
+}
+
+void
+JavaBridge::setSharedTimerCallback(void (*f)())
+{
+ LOG_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);
+ env->CallVoidMethod(obj.get(), mSignalFuncPtrQueue);
+}
+
+WTF::Vector<WTF::String>JavaBridge::getSupportedKeyStrengthList() {
+ WTF::Vector<WTF::String> list;
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject obj = javaObject(env);
+ 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();
+ jstring jChallenge = wtfStringToJstring(env, challenge);
+ const WTF::String& urlStr = url.string();
+ jstring jUrl = wtfStringToJstring(env, urlStr);
+ AutoJObject obj = javaObject(env);
+ 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();
+ jstring jUri = wtfStringToJstring(env, uri);
+ AutoJObject obj = javaObject(env);
+ jstring path = static_cast<jstring>(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));
+ LOG_ASSERT(javaBridge, "Finalize should not be called twice for the same java bridge!");
+ LOGV("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)
+ {
+#ifdef ANDROID_INSTRUMENT
+ TimeCounter::start(TimeCounter::SharedTimerTimeCounter);
+#endif
+ SkAutoMemoryUsageProbe mup("JavaBridge::sharedTimerFired");
+ sSharedTimerFiredCallback();
+#ifdef ANDROID_INSTRUMENT
+ TimeCounter::record(TimeCounter::SharedTimerTimeCounter, __FUNCTION__);
+#endif
+ }
+}
+
+void JavaBridge::SetCacheSize(JNIEnv* env, jobject obj, jint bytes)
+{
+ WebCore::cache()->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<WTF::String> 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<WTF::String> namesSet;
+ while (env->CallBooleanMethod(iter, hasNext)) {
+ jstring name = static_cast<jstring>(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)
+{
+#if USE(CHROME_NETWORK_STACK)
+ std::string proxy = jstringToStdString(env, newProxy);
+ WebCache::get(false)->proxy()->UpdateProxySettings(proxy);
+ WebCache::get(true)->proxy()->UpdateProxySettings(proxy);
+#endif
+}
+
+
+// ----------------------------------------------------------------------------
+
+/*
+ * 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 },
+ { "updateProxy", "(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 */
diff --git a/Source/WebKit/android/jni/JavaSharedClient.cpp b/Source/WebKit/android/jni/JavaSharedClient.cpp
new file mode 100644
index 0000000..e884c99
--- /dev/null
+++ b/Source/WebKit/android/jni/JavaSharedClient.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2007, 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.
+ */
+
+#include "config.h"
+#include "FileSystemClient.h"
+#include "JavaSharedClient.h"
+#include "TimerClient.h"
+#include "SkDeque.h"
+#include "SkThread.h"
+
+namespace android {
+ TimerClient* JavaSharedClient::GetTimerClient()
+ {
+ return gTimerClient;
+ }
+
+ CookieClient* JavaSharedClient::GetCookieClient()
+ {
+ return gCookieClient;
+ }
+
+ PluginClient* JavaSharedClient::GetPluginClient()
+ {
+ return gPluginClient;
+ }
+
+ KeyGeneratorClient* JavaSharedClient::GetKeyGeneratorClient()
+ {
+ return gKeyGeneratorClient;
+ }
+
+ FileSystemClient* JavaSharedClient::GetFileSystemClient()
+ {
+ return gFileSystemClient;
+ }
+
+ void JavaSharedClient::SetTimerClient(TimerClient* client)
+ {
+ gTimerClient = client;
+ }
+
+ void JavaSharedClient::SetCookieClient(CookieClient* client)
+ {
+ gCookieClient = client;
+ }
+
+ void JavaSharedClient::SetPluginClient(PluginClient* client)
+ {
+ gPluginClient = client;
+ }
+
+ void JavaSharedClient::SetKeyGeneratorClient(KeyGeneratorClient* client)
+ {
+ gKeyGeneratorClient = client;
+ }
+
+ void JavaSharedClient::SetFileSystemClient(FileSystemClient* client)
+ {
+ gFileSystemClient = client;
+ }
+
+ TimerClient* JavaSharedClient::gTimerClient = NULL;
+ CookieClient* JavaSharedClient::gCookieClient = NULL;
+ PluginClient* JavaSharedClient::gPluginClient = NULL;
+ KeyGeneratorClient* JavaSharedClient::gKeyGeneratorClient = NULL;
+ FileSystemClient* JavaSharedClient::gFileSystemClient = NULL;
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ struct FuncPtrRec {
+ void (*fProc)(void* payload);
+ void* fPayload;
+ };
+
+ static SkMutex gFuncPtrQMutex;
+ static SkDeque gFuncPtrQ(sizeof(FuncPtrRec));
+
+ void JavaSharedClient::EnqueueFunctionPtr(void (*proc)(void* payload),
+ void* payload)
+ {
+ gFuncPtrQMutex.acquire();
+
+ FuncPtrRec* rec = (FuncPtrRec*)gFuncPtrQ.push_back();
+ rec->fProc = proc;
+ rec->fPayload = payload;
+
+ gFuncPtrQMutex.release();
+
+ gTimerClient->signalServiceFuncPtrQueue();
+ }
+
+ void JavaSharedClient::ServiceFunctionPtrQueue()
+ {
+ for (;;) {
+ void (*proc)(void*) = 0;
+ void* payload = 0;
+ const FuncPtrRec* rec;
+
+ // we have to copy the proc/payload (if present). we do this so we
+ // don't call the proc inside the mutex (possible deadlock!)
+ gFuncPtrQMutex.acquire();
+ rec = (const FuncPtrRec*)gFuncPtrQ.front();
+ if (rec) {
+ proc = rec->fProc;
+ payload = rec->fPayload;
+ gFuncPtrQ.pop_front();
+ }
+ gFuncPtrQMutex.release();
+
+ if (!rec)
+ break;
+ proc(payload);
+ }
+ }
+}
diff --git a/Source/WebKit/android/jni/JavaSharedClient.h b/Source/WebKit/android/jni/JavaSharedClient.h
new file mode 100644
index 0000000..9a09280
--- /dev/null
+++ b/Source/WebKit/android/jni/JavaSharedClient.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2007, 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.
+ */
+
+#ifndef JAVA_SHARED_CLIENT_H
+#define JAVA_SHARED_CLIENT_H
+
+namespace android {
+
+ class TimerClient;
+ class CookieClient;
+ class PluginClient;
+ class KeyGeneratorClient;
+ class FileSystemClient;
+
+ class JavaSharedClient
+ {
+ public:
+ static TimerClient* GetTimerClient();
+ static CookieClient* GetCookieClient();
+ static PluginClient* GetPluginClient();
+ static KeyGeneratorClient* GetKeyGeneratorClient();
+ static FileSystemClient* GetFileSystemClient();
+
+ static void SetTimerClient(TimerClient* client);
+ static void SetCookieClient(CookieClient* client);
+ static void SetPluginClient(PluginClient* client);
+ static void SetKeyGeneratorClient(KeyGeneratorClient* client);
+ static void SetFileSystemClient(FileSystemClient* client);
+
+ // can be called from any thread, to be executed in webkit thread
+ static void EnqueueFunctionPtr(void (*proc)(void*), void* payload);
+ // only call this from webkit thread
+ static void ServiceFunctionPtrQueue();
+
+ private:
+ static TimerClient* gTimerClient;
+ static CookieClient* gCookieClient;
+ static PluginClient* gPluginClient;
+ static KeyGeneratorClient* gKeyGeneratorClient;
+ static FileSystemClient* gFileSystemClient;
+ };
+}
+#endif
diff --git a/Source/WebKit/android/jni/JniUtil.cpp b/Source/WebKit/android/jni/JniUtil.cpp
new file mode 100644
index 0000000..ee1e3f9
--- /dev/null
+++ b/Source/WebKit/android/jni/JniUtil.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2010, 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.
+ */
+
+#include "config.h"
+
+#include "ChromiumIncludes.h"
+#include <JNIHelp.h>
+
+namespace android {
+
+static const char* javaJniUtilClass = "android/webkit/JniUtil";
+
+static bool useChromiumHttpStack(JNIEnv*, jobject)
+{
+#if USE(CHROME_NETWORK_STACK)
+ return true;
+#else
+ return false;
+#endif
+}
+
+static JNINativeMethod gJniUtilMethods[] = {
+ { "nativeUseChromiumHttpStack", "()Z", (void*) useChromiumHttpStack },
+};
+
+int registerJniUtil(JNIEnv* env)
+{
+#ifndef NDEBUG
+ jclass jniUtil = env->FindClass(javaJniUtilClass);
+ LOG_ASSERT(jniUtil, "Unable to find class");
+ env->DeleteLocalRef(jniUtil);
+#endif
+ return jniRegisterNativeMethods(env, javaJniUtilClass, gJniUtilMethods, NELEM(gJniUtilMethods));
+}
+
+} // namespace android
diff --git a/Source/WebKit/android/jni/MIMETypeRegistry.cpp b/Source/WebKit/android/jni/MIMETypeRegistry.cpp
new file mode 100644
index 0000000..40f8cef
--- /dev/null
+++ b/Source/WebKit/android/jni/MIMETypeRegistry.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2010, 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 "WebCore"
+
+#include "config.h"
+#include "MIMETypeRegistry.h"
+
+#include "PlatformString.h"
+#include "WebCoreJni.h"
+
+#include <JNIUtility.h>
+#include <jni.h>
+#include <utils/Log.h>
+
+using namespace android;
+
+namespace WebCore {
+
+String MIMETypeRegistry::getMIMETypeForExtension(const String& ext)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jclass mimeClass = env->FindClass("android/webkit/MimeTypeMap");
+ LOG_ASSERT(mimeClass, "Could not find class MimeTypeMap");
+ jmethodID mimeTypeFromExtension = env->GetStaticMethodID(mimeClass,
+ "mimeTypeFromExtension",
+ "(Ljava/lang/String;)Ljava/lang/String;");
+ LOG_ASSERT(mimeTypeFromExtension,
+ "Could not find method mimeTypeFromExtension");
+ jstring extString = wtfStringToJstring(env, ext);
+ jobject mimeType = env->CallStaticObjectMethod(mimeClass,
+ mimeTypeFromExtension, extString);
+ String result = android::jstringToWtfString(env, (jstring) mimeType);
+ env->DeleteLocalRef(mimeClass);
+ env->DeleteLocalRef(extString);
+ env->DeleteLocalRef(mimeType);
+ return result;
+}
+
+bool MIMETypeRegistry::isApplicationPluginMIMEType(const String&)
+{
+ return false;
+}
+
+}
diff --git a/Source/WebKit/android/jni/MockGeolocation.cpp b/Source/WebKit/android/jni/MockGeolocation.cpp
new file mode 100755
index 0000000..1370715
--- /dev/null
+++ b/Source/WebKit/android/jni/MockGeolocation.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2009, 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.
+ */
+
+// The functions in this file are used to configure the mock GeolocationService
+// for the LayoutTests.
+
+#include "config.h"
+
+#include "Coordinates.h"
+#include "GeolocationServiceMock.h"
+#include "Geoposition.h"
+#include "JavaSharedClient.h"
+#include "PositionError.h"
+#include "WebCoreJni.h"
+#include <JNIHelp.h>
+#include <JNIUtility.h>
+#include <wtf/CurrentTime.h>
+
+using namespace WebCore;
+
+namespace android {
+
+static const char* javaMockGeolocationClass = "android/webkit/MockGeolocation";
+
+static void setPosition(JNIEnv* env, jobject, double latitude, double longitude, double accuracy)
+{
+ RefPtr<Coordinates> coordinates = Coordinates::create(latitude,
+ longitude,
+ false, 0.0, // altitude,
+ accuracy,
+ false, 0.0, // altitudeAccuracy,
+ false, 0.0, // heading
+ false, 0.0); // speed
+ RefPtr<Geoposition> position = Geoposition::create(coordinates.release(), WTF::currentTimeMS());
+ GeolocationServiceMock::setPosition(position.release());
+}
+
+static void setError(JNIEnv* env, jobject, int code, jstring message)
+{
+ PositionError::ErrorCode codeEnum = static_cast<PositionError::ErrorCode>(code);
+ String messageString = jstringToWtfString(env, message);
+ RefPtr<PositionError> error = PositionError::create(codeEnum, messageString);
+ GeolocationServiceMock::setError(error.release());
+}
+
+static JNINativeMethod gMockGeolocationMethods[] = {
+ { "nativeSetPosition", "(DDD)V", (void*) setPosition },
+ { "nativeSetError", "(ILjava/lang/String;)V", (void*) setError }
+};
+
+int registerMockGeolocation(JNIEnv* env)
+{
+#ifndef NDEBUG
+ jclass mockGeolocation = env->FindClass(javaMockGeolocationClass);
+ LOG_ASSERT(mockGeolocation, "Unable to find class");
+ env->DeleteLocalRef(mockGeolocation);
+#endif
+
+ return jniRegisterNativeMethods(env, javaMockGeolocationClass, gMockGeolocationMethods, NELEM(gMockGeolocationMethods));
+}
+
+}
diff --git a/Source/WebKit/android/jni/PictureSet.cpp b/Source/WebKit/android/jni/PictureSet.cpp
new file mode 100644
index 0000000..6dafd26
--- /dev/null
+++ b/Source/WebKit/android/jni/PictureSet.cpp
@@ -0,0 +1,676 @@
+/*
+ * Copyright 2008, 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_NDEBUG 0
+#define LOG_TAG "pictureset"
+
+//#include <config.h>
+#include "CachedPrefix.h"
+#include "android_graphics.h"
+#include "PictureSet.h"
+#include "SkBounder.h"
+#include "SkCanvas.h"
+#include "SkPicture.h"
+#include "SkRect.h"
+#include "SkRegion.h"
+#include "SkStream.h"
+#include "TimeCounter.h"
+
+#define MAX_DRAW_TIME 100
+#define MIN_SPLITTABLE 400
+
+#if PICTURE_SET_DEBUG
+class MeasureStream : public SkWStream {
+public:
+ MeasureStream() : mTotal(0) {}
+ virtual bool write(const void* , size_t size) {
+ mTotal += size;
+ return true;
+ }
+ size_t mTotal;
+};
+#endif
+
+namespace android {
+
+PictureSet::PictureSet()
+{
+ mWidth = mHeight = 0;
+}
+
+PictureSet::~PictureSet()
+{
+ clear();
+}
+
+void PictureSet::add(const Pictures* temp)
+{
+ Pictures pictureAndBounds = *temp;
+ SkSafeRef(pictureAndBounds.mPicture);
+ pictureAndBounds.mWroteElapsed = false;
+ mPictures.append(pictureAndBounds);
+}
+
+void PictureSet::add(const SkRegion& area, SkPicture* picture,
+ uint32_t elapsed, bool split, bool empty)
+{
+ DBG_SET_LOGD("%p area={%d,%d,r=%d,b=%d} pict=%p elapsed=%d split=%d", this,
+ area.getBounds().fLeft, area.getBounds().fTop,
+ area.getBounds().fRight, area.getBounds().fBottom, picture,
+ elapsed, split);
+ SkSafeRef(picture);
+ /* if nothing is drawn beneath part of the new picture, mark it as a base */
+ SkRegion diff = SkRegion(area);
+ Pictures* last = mPictures.end();
+ for (Pictures* working = mPictures.begin(); working != last; working++)
+ diff.op(working->mArea, SkRegion::kDifference_Op);
+ Pictures pictureAndBounds = {area, picture, area.getBounds(),
+ elapsed, split, false, diff.isEmpty() == false, empty};
+ mPictures.append(pictureAndBounds);
+}
+
+/*
+Pictures are discarded when they are fully drawn over.
+When a picture is partially drawn over, it is discarded if it is not a base, and
+its rectangular bounds is reduced if it is a base.
+*/
+bool PictureSet::build()
+{
+ bool rebuild = false;
+ DBG_SET_LOGD("%p", this);
+ // walk pictures back to front, removing or trimming obscured ones
+ SkRegion drawn;
+ SkRegion inval;
+ Pictures* first = mPictures.begin();
+ Pictures* last = mPictures.end();
+ Pictures* working;
+ bool checkForNewBases = false;
+ for (working = last; working != first; ) {
+ --working;
+ SkRegion& area = working->mArea;
+ SkRegion visibleArea(area);
+ visibleArea.op(drawn, SkRegion::kDifference_Op);
+#if PICTURE_SET_DEBUG
+ const SkIRect& a = area.getBounds();
+ const SkIRect& d = drawn.getBounds();
+ const SkIRect& i = inval.getBounds();
+ const SkIRect& v = visibleArea.getBounds();
+ DBG_SET_LOGD("%p [%d] area={%d,%d,r=%d,b=%d} drawn={%d,%d,r=%d,b=%d}"
+ " inval={%d,%d,r=%d,b=%d} vis={%d,%d,r=%d,b=%d}",
+ this, working - first,
+ a.fLeft, a.fTop, a.fRight, a.fBottom,
+ d.fLeft, d.fTop, d.fRight, d.fBottom,
+ i.fLeft, i.fTop, i.fRight, i.fBottom,
+ v.fLeft, v.fTop, v.fRight, v.fBottom);
+#endif
+ bool tossPicture = false;
+ if (working->mBase == false) {
+ if (area != visibleArea) {
+ if (visibleArea.isEmpty() == false) {
+ DBG_SET_LOGD("[%d] partially overdrawn", working - first);
+ inval.op(visibleArea, SkRegion::kUnion_Op);
+ } else
+ DBG_SET_LOGD("[%d] fully hidden", working - first);
+ area.setEmpty();
+ tossPicture = true;
+ }
+ } else {
+ const SkIRect& visibleBounds = visibleArea.getBounds();
+ const SkIRect& areaBounds = area.getBounds();
+ if (visibleBounds != areaBounds) {
+ DBG_SET_LOGD("[%d] base to be reduced", working - first);
+ area.setRect(visibleBounds);
+ checkForNewBases = tossPicture = true;
+ }
+ if (area.intersects(inval)) {
+ DBG_SET_LOGD("[%d] base to be redrawn", working - first);
+ tossPicture = true;
+ }
+ }
+ if (tossPicture) {
+ SkSafeUnref(working->mPicture);
+ working->mPicture = NULL; // mark to redraw
+ }
+ if (working->mPicture == NULL) // may have been set to null elsewhere
+ rebuild = true;
+ drawn.op(area, SkRegion::kUnion_Op);
+ }
+ // collapse out empty regions
+ Pictures* writer = first;
+ for (working = first; working != last; working++) {
+ if (working->mArea.isEmpty())
+ continue;
+ *writer++ = *working;
+ }
+#if PICTURE_SET_DEBUG
+ if ((unsigned) (writer - first) != mPictures.size())
+ DBG_SET_LOGD("shrink=%d (was %d)", writer - first, mPictures.size());
+#endif
+ mPictures.shrink(writer - first);
+ /* When a base is discarded because it was entirely drawn over, all
+ remaining pictures are checked to see if one has become a base. */
+ if (checkForNewBases) {
+ drawn.setEmpty();
+ Pictures* last = mPictures.end();
+ for (working = mPictures.begin(); working != last; working++) {
+ SkRegion& area = working->mArea;
+ if (drawn.contains(working->mArea) == false) {
+ working->mBase = true;
+ DBG_SET_LOGD("[%d] new base", working - mPictures.begin());
+ }
+ drawn.op(working->mArea, SkRegion::kUnion_Op);
+ }
+ }
+ validate(__FUNCTION__);
+ return rebuild;
+}
+
+void PictureSet::checkDimensions(int width, int height, SkRegion* inval)
+{
+ if (mWidth == width && mHeight == height)
+ return;
+ DBG_SET_LOGD("%p old:(w=%d,h=%d) new:(w=%d,h=%d)", this,
+ mWidth, mHeight, width, height);
+ if (mWidth == width && height > mHeight) { // only grew vertically
+ SkIRect rect;
+ rect.set(0, mHeight, width, height - mHeight);
+ inval->op(rect, SkRegion::kUnion_Op);
+ } else {
+ clear(); // if both width/height changed, clear the old cache
+ inval->setRect(0, 0, width, height);
+ }
+ mWidth = width;
+ mHeight = height;
+}
+
+void PictureSet::clear()
+{
+ DBG_SET_LOG("");
+ Pictures* last = mPictures.end();
+ for (Pictures* working = mPictures.begin(); working != last; working++) {
+ working->mArea.setEmpty();
+ SkSafeUnref(working->mPicture);
+ }
+ mPictures.clear();
+ mWidth = mHeight = 0;
+}
+
+bool PictureSet::draw(SkCanvas* canvas)
+{
+ validate(__FUNCTION__);
+ Pictures* first = mPictures.begin();
+ Pictures* last = mPictures.end();
+ Pictures* working;
+ SkRect bounds;
+ if (canvas->getClipBounds(&bounds) == false)
+ return false;
+ SkIRect irect;
+ bounds.roundOut(&irect);
+ for (working = last; working != first; ) {
+ --working;
+ if (working->mArea.contains(irect)) {
+#if PICTURE_SET_DEBUG
+ const SkIRect& b = working->mArea.getBounds();
+ DBG_SET_LOGD("contains working->mArea={%d,%d,%d,%d}"
+ " irect={%d,%d,%d,%d}", b.fLeft, b.fTop, b.fRight, b.fBottom,
+ irect.fLeft, irect.fTop, irect.fRight, irect.fBottom);
+#endif
+ first = working;
+ break;
+ }
+ }
+ DBG_SET_LOGD("%p first=%d last=%d", this, first - mPictures.begin(),
+ last - mPictures.begin());
+ uint32_t maxElapsed = 0;
+ for (working = first; working != last; working++) {
+ const SkRegion& area = working->mArea;
+ if (area.quickReject(irect)) {
+#if PICTURE_SET_DEBUG
+ const SkIRect& b = area.getBounds();
+ DBG_SET_LOGD("[%d] %p quickReject working->mArea={%d,%d,%d,%d}"
+ " irect={%d,%d,%d,%d}", working - first, working,
+ b.fLeft, b.fTop, b.fRight, b.fBottom,
+ irect.fLeft, irect.fTop, irect.fRight, irect.fBottom);
+#endif
+ working->mElapsed = 0;
+ continue;
+ }
+ int saved = canvas->save();
+ SkRect pathBounds;
+ if (area.isComplex()) {
+ SkPath pathClip;
+ area.getBoundaryPath(&pathClip);
+ canvas->clipPath(pathClip);
+ pathBounds = pathClip.getBounds();
+ } else {
+ pathBounds.set(area.getBounds());
+ canvas->clipRect(pathBounds);
+ }
+ canvas->translate(pathBounds.fLeft, pathBounds.fTop);
+ canvas->save();
+ uint32_t startTime = getThreadMsec();
+ canvas->drawPicture(*working->mPicture);
+ size_t elapsed = working->mElapsed = getThreadMsec() - startTime;
+ working->mWroteElapsed = true;
+ if (maxElapsed < elapsed && (pathBounds.width() >= MIN_SPLITTABLE ||
+ pathBounds.height() >= MIN_SPLITTABLE))
+ maxElapsed = elapsed;
+ canvas->restoreToCount(saved);
+#define DRAW_TEST_IMAGE 01
+#if DRAW_TEST_IMAGE && PICTURE_SET_DEBUG
+ SkColor color = 0x3f000000 | (0xffffff & (unsigned) working);
+ canvas->drawColor(color);
+ SkPaint paint;
+ color ^= 0x00ffffff;
+ paint.setColor(color);
+ char location[256];
+ for (int x = area.getBounds().fLeft & ~0x3f;
+ x < area.getBounds().fRight; x += 0x40) {
+ for (int y = area.getBounds().fTop & ~0x3f;
+ y < area.getBounds().fBottom; y += 0x40) {
+ int len = snprintf(location, sizeof(location) - 1, "(%d,%d)", x, y);
+ canvas->drawText(location, len, x, y, paint);
+ }
+ }
+#endif
+ DBG_SET_LOGD("[%d] %p working->mArea={%d,%d,%d,%d} elapsed=%d base=%s",
+ working - first, working,
+ area.getBounds().fLeft, area.getBounds().fTop,
+ area.getBounds().fRight, area.getBounds().fBottom,
+ working->mElapsed, working->mBase ? "true" : "false");
+ }
+ // dump(__FUNCTION__);
+ return maxElapsed >= MAX_DRAW_TIME;
+}
+
+void PictureSet::dump(const char* label) const
+{
+#if PICTURE_SET_DUMP
+ DBG_SET_LOGD("%p %s (%d) (w=%d,h=%d)", this, label, mPictures.size(),
+ mWidth, mHeight);
+ const Pictures* last = mPictures.end();
+ for (const Pictures* working = mPictures.begin(); working != last; working++) {
+ const SkIRect& bounds = working->mArea.getBounds();
+ const SkIRect& unsplit = working->mUnsplit;
+ MeasureStream measure;
+ if (working->mPicture != NULL)
+ working->mPicture->serialize(&measure);
+ LOGD(" [%d]"
+ " mArea.bounds={%d,%d,r=%d,b=%d}"
+ " mPicture=%p"
+ " mUnsplit={%d,%d,r=%d,b=%d}"
+ " mElapsed=%d"
+ " mSplit=%s"
+ " mWroteElapsed=%s"
+ " mBase=%s"
+ " pict-size=%d",
+ working - mPictures.begin(),
+ bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom,
+ working->mPicture,
+ unsplit.fLeft, unsplit.fTop, unsplit.fRight, unsplit.fBottom,
+ working->mElapsed, working->mSplit ? "true" : "false",
+ working->mWroteElapsed ? "true" : "false",
+ working->mBase ? "true" : "false",
+ measure.mTotal);
+ }
+#endif
+}
+
+class IsEmptyBounder : public SkBounder {
+ virtual bool onIRect(const SkIRect& rect) {
+ return false;
+ }
+};
+
+class IsEmptyCanvas : public SkCanvas {
+public:
+ IsEmptyCanvas(SkBounder* bounder, SkPicture* picture) :
+ mPicture(picture), mEmpty(true) {
+ setBounder(bounder);
+ }
+
+ void notEmpty() {
+ mEmpty = false;
+ mPicture->abortPlayback();
+ }
+
+ virtual bool clipPath(const SkPath&, SkRegion::Op) {
+ // this can be expensive to actually do, and doesn't affect the
+ // question of emptiness, so we make it a no-op
+ return true;
+ }
+
+ virtual void commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* rect,
+ const SkMatrix& , const SkPaint& ) {
+ if (bitmap.width() <= 1 || bitmap.height() <= 1)
+ return;
+ DBG_SET_LOGD("abort {%d,%d}", bitmap.width(), bitmap.height());
+ notEmpty();
+ }
+
+ virtual void drawPaint(const SkPaint& paint) {
+ }
+
+ virtual void drawPath(const SkPath& , const SkPaint& paint) {
+ DBG_SET_LOG("abort");
+ notEmpty();
+ }
+
+ virtual void drawPoints(PointMode , size_t , const SkPoint [],
+ const SkPaint& paint) {
+ }
+
+ virtual void drawRect(const SkRect& , const SkPaint& paint) {
+ // wait for visual content
+ if (paint.getColor() != SK_ColorWHITE)
+ notEmpty();
+ }
+
+ virtual void drawSprite(const SkBitmap& , int , int ,
+ const SkPaint* paint = NULL) {
+ DBG_SET_LOG("abort");
+ notEmpty();
+ }
+
+ virtual void drawText(const void* , size_t byteLength, SkScalar ,
+ SkScalar , const SkPaint& paint) {
+ DBG_SET_LOGD("abort %d", byteLength);
+ notEmpty();
+ }
+
+ virtual void drawPosText(const void* , size_t byteLength,
+ const SkPoint [], const SkPaint& paint) {
+ DBG_SET_LOGD("abort %d", byteLength);
+ notEmpty();
+ }
+
+ virtual void drawPosTextH(const void* , size_t byteLength,
+ const SkScalar [], SkScalar ,
+ const SkPaint& paint) {
+ DBG_SET_LOGD("abort %d", byteLength);
+ notEmpty();
+ }
+
+ virtual void drawTextOnPath(const void* , size_t byteLength,
+ const SkPath& , const SkMatrix* ,
+ const SkPaint& paint) {
+ DBG_SET_LOGD("abort %d", byteLength);
+ notEmpty();
+ }
+
+ virtual void drawPicture(SkPicture& picture) {
+ SkCanvas::drawPicture(picture);
+ }
+
+ SkPicture* mPicture;
+ bool mEmpty;
+};
+
+bool PictureSet::emptyPicture(SkPicture* picture) const
+{
+ IsEmptyBounder isEmptyBounder;
+ IsEmptyCanvas checker(&isEmptyBounder, picture);
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, mWidth, mHeight);
+ checker.setBitmapDevice(bitmap);
+ checker.drawPicture(*picture);
+ return checker.mEmpty;
+}
+
+bool PictureSet::isEmpty() const
+{
+ const Pictures* last = mPictures.end();
+ for (const Pictures* working = mPictures.begin(); working != last; working++) {
+ if (!working->mEmpty)
+ return false;
+ }
+ return true;
+}
+
+bool PictureSet::reuseSubdivided(const SkRegion& inval)
+{
+ validate(__FUNCTION__);
+ if (inval.isComplex())
+ return false;
+ Pictures* working, * last = mPictures.end();
+ const SkIRect& invalBounds = inval.getBounds();
+ bool steal = false;
+ for (working = mPictures.begin(); working != last; working++) {
+ if (working->mSplit && invalBounds == working->mUnsplit) {
+ steal = true;
+ continue;
+ }
+ if (steal == false)
+ continue;
+ SkRegion temp = SkRegion(inval);
+ temp.op(working->mArea, SkRegion::kIntersect_Op);
+ if (temp.isEmpty() || temp == working->mArea)
+ continue;
+ return false;
+ }
+ if (steal == false)
+ return false;
+ for (working = mPictures.begin(); working != last; working++) {
+ if ((working->mSplit == false || invalBounds != working->mUnsplit) &&
+ inval.contains(working->mArea) == false)
+ continue;
+ SkSafeUnref(working->mPicture);
+ working->mPicture = NULL;
+ }
+ return true;
+}
+
+void PictureSet::set(const PictureSet& src)
+{
+ DBG_SET_LOGD("start %p src=%p", this, &src);
+ clear();
+ mWidth = src.mWidth;
+ mHeight = src.mHeight;
+ const Pictures* last = src.mPictures.end();
+ for (const Pictures* working = src.mPictures.begin(); working != last; working++)
+ add(working);
+ // dump(__FUNCTION__);
+ validate(__FUNCTION__);
+ DBG_SET_LOG("end");
+}
+
+void PictureSet::setDrawTimes(const PictureSet& src)
+{
+ validate(__FUNCTION__);
+ if (mWidth != src.mWidth || mHeight != src.mHeight)
+ return;
+ Pictures* last = mPictures.end();
+ Pictures* working = mPictures.begin();
+ if (working == last)
+ return;
+ const Pictures* srcLast = src.mPictures.end();
+ const Pictures* srcWorking = src.mPictures.begin();
+ for (; srcWorking != srcLast; srcWorking++) {
+ if (srcWorking->mWroteElapsed == false)
+ continue;
+ while ((srcWorking->mArea != working->mArea ||
+ srcWorking->mPicture != working->mPicture)) {
+ if (++working == last)
+ return;
+ }
+ DBG_SET_LOGD("%p [%d] [%d] {%d,%d,r=%d,b=%d} working->mElapsed=%d <- %d",
+ this, working - mPictures.begin(), srcWorking - src.mPictures.begin(),
+ working->mArea.getBounds().fLeft, working->mArea.getBounds().fTop,
+ working->mArea.getBounds().fRight, working->mArea.getBounds().fBottom,
+ working->mElapsed, srcWorking->mElapsed);
+ working->mElapsed = srcWorking->mElapsed;
+ }
+}
+
+void PictureSet::setPicture(size_t i, SkPicture* p)
+{
+ SkSafeUnref(mPictures[i].mPicture);
+ mPictures[i].mPicture = p;
+ mPictures[i].mEmpty = emptyPicture(p);
+}
+
+void PictureSet::split(PictureSet* out) const
+{
+ dump(__FUNCTION__);
+ DBG_SET_LOGD("%p", this);
+ SkIRect totalBounds;
+ out->mWidth = mWidth;
+ out->mHeight = mHeight;
+ totalBounds.set(0, 0, mWidth, mHeight);
+ SkRegion* total = new SkRegion(totalBounds);
+ const Pictures* last = mPictures.end();
+ const Pictures* working;
+ uint32_t balance = 0;
+ int multiUnsplitFastPictures = 0; // > 1 has more than 1
+ for (working = mPictures.begin(); working != last; working++) {
+ if (working->mElapsed >= MAX_DRAW_TIME || working->mSplit)
+ continue;
+ if (++multiUnsplitFastPictures > 1)
+ break;
+ }
+ for (working = mPictures.begin(); working != last; working++) {
+ uint32_t elapsed = working->mElapsed;
+ if (elapsed < MAX_DRAW_TIME) {
+ bool split = working->mSplit;
+ DBG_SET_LOGD("elapsed=%d working=%p total->getBounds()="
+ "{%d,%d,r=%d,b=%d} split=%s", elapsed, working,
+ total->getBounds().fLeft, total->getBounds().fTop,
+ total->getBounds().fRight, total->getBounds().fBottom,
+ split ? "true" : "false");
+ if (multiUnsplitFastPictures <= 1 || split) {
+ total->op(working->mArea, SkRegion::kDifference_Op);
+ out->add(working->mArea, working->mPicture, elapsed, split,
+ working->mEmpty);
+ } else if (balance < elapsed)
+ balance = elapsed;
+ continue;
+ }
+ total->op(working->mArea, SkRegion::kDifference_Op);
+ const SkIRect& bounds = working->mArea.getBounds();
+ int width = bounds.width();
+ int height = bounds.height();
+ int across = 1;
+ int down = 1;
+ while (height >= MIN_SPLITTABLE || width >= MIN_SPLITTABLE) {
+ if (height >= width) {
+ height >>= 1;
+ down <<= 1;
+ } else {
+ width >>= 1;
+ across <<= 1 ;
+ }
+ if ((elapsed >>= 1) < MAX_DRAW_TIME)
+ break;
+ }
+ width = bounds.width();
+ height = bounds.height();
+ int top = bounds.fTop;
+ for (int indexY = 0; indexY < down; ) {
+ int bottom = bounds.fTop + height * ++indexY / down;
+ int left = bounds.fLeft;
+ for (int indexX = 0; indexX < across; ) {
+ int right = bounds.fLeft + width * ++indexX / across;
+ SkIRect cBounds;
+ cBounds.set(left, top, right, bottom);
+ out->add(SkRegion(cBounds), (across | down) != 1 ? NULL :
+ working->mPicture, elapsed, true,
+ (across | down) != 1 ? false : working->mEmpty);
+ left = right;
+ }
+ top = bottom;
+ }
+ }
+ DBG_SET_LOGD("%p w=%d h=%d total->isEmpty()=%s multiUnsplitFastPictures=%d",
+ this, mWidth, mHeight, total->isEmpty() ? "true" : "false",
+ multiUnsplitFastPictures);
+ if (!total->isEmpty() && multiUnsplitFastPictures > 1)
+ out->add(*total, NULL, balance, false, false);
+ delete total;
+ validate(__FUNCTION__);
+ out->dump("split-out");
+}
+
+bool PictureSet::validate(const char* funct) const
+{
+ bool valid = true;
+#if PICTURE_SET_VALIDATE
+ SkRegion all;
+ const Pictures* first = mPictures.begin();
+ for (const Pictures* working = mPictures.end(); working != first; ) {
+ --working;
+ const SkPicture* pict = working->mPicture;
+ const SkRegion& area = working->mArea;
+ const SkIRect& bounds = area.getBounds();
+ bool localValid = false;
+ if (working->mUnsplit.isEmpty())
+ LOGD("%s working->mUnsplit.isEmpty()", funct);
+ else if (working->mUnsplit.contains(bounds) == false)
+ LOGD("%s working->mUnsplit.contains(bounds) == false", funct);
+ else if (working->mElapsed >= 1000)
+ LOGD("%s working->mElapsed >= 1000", funct);
+ else if ((working->mSplit & 0xfe) != 0)
+ LOGD("%s (working->mSplit & 0xfe) != 0", funct);
+ else if ((working->mWroteElapsed & 0xfe) != 0)
+ LOGD("%s (working->mWroteElapsed & 0xfe) != 0", funct);
+ else if (pict != NULL) {
+ int pictWidth = pict->width();
+ int pictHeight = pict->height();
+ if (pictWidth < bounds.width())
+ LOGD("%s pictWidth=%d < bounds.width()=%d", funct, pictWidth, bounds.width());
+ else if (pictHeight < bounds.height())
+ LOGD("%s pictHeight=%d < bounds.height()=%d", funct, pictHeight, bounds.height());
+ else if (working->mArea.isEmpty())
+ LOGD("%s working->mArea.isEmpty()", funct);
+ else
+ localValid = true;
+ } else
+ localValid = true;
+ working->mArea.validate();
+ if (localValid == false) {
+ if (all.contains(area) == true)
+ LOGD("%s all.contains(area) == true", funct);
+ else
+ localValid = true;
+ }
+ valid &= localValid;
+ all.op(area, SkRegion::kUnion_Op);
+ }
+ const SkIRect& allBounds = all.getBounds();
+ if (valid) {
+ valid = false;
+ if (allBounds.width() != mWidth)
+ LOGD("%s allBounds.width()=%d != mWidth=%d", funct, allBounds.width(), mWidth);
+ else if (allBounds.height() != mHeight)
+ LOGD("%s allBounds.height()=%d != mHeight=%d", funct, allBounds.height(), mHeight);
+ else
+ valid = true;
+ }
+ while (valid == false)
+ ;
+#endif
+ return valid;
+}
+
+} /* namespace android */
diff --git a/Source/WebKit/android/jni/PictureSet.h b/Source/WebKit/android/jni/PictureSet.h
new file mode 100644
index 0000000..b177958
--- /dev/null
+++ b/Source/WebKit/android/jni/PictureSet.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2008, 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.
+ */
+
+#ifndef PICTURESET_H
+#define PICTURESET_H
+
+#define PICTURE_SET_DUMP 0
+#define PICTURE_SET_DEBUG 0
+#define PICTURE_SET_VALIDATE 0
+
+#if PICTURE_SET_DEBUG
+#define DBG_SET_LOG(message) LOGD("%s %s", __FUNCTION__, message)
+#define DBG_SET_LOGD(format, ...) LOGD("%s " format, __FUNCTION__, __VA_ARGS__)
+#define DEBUG_SET_UI_LOGD(...) LOGD(__VA_ARGS__)
+#else
+#define DBG_SET_LOG(message) ((void)0)
+#define DBG_SET_LOGD(format, ...) ((void)0)
+#define DEBUG_SET_UI_LOGD(...) ((void)0)
+#endif
+
+#include "jni.h"
+#include "SkRegion.h"
+#include <wtf/Vector.h>
+
+class SkCanvas;
+class SkPicture;
+class SkIRect;
+
+namespace android {
+
+ class PictureSet {
+ public:
+ PictureSet();
+ PictureSet(const PictureSet& src) { set(src); }
+ virtual ~PictureSet();
+ void add(const SkRegion& area, SkPicture* picture,
+ uint32_t elapsed, bool split)
+ {
+ add(area, picture, elapsed, split, emptyPicture(picture));
+ }
+ void add(const SkRegion& area, SkPicture* picture,
+ uint32_t elapsed, bool split, bool empty);
+ const SkIRect& bounds(size_t i) const {
+ return mPictures[i].mArea.getBounds(); }
+ bool build();
+ // Update mWidth/mHeight, and adds any additional inval region
+ void checkDimensions(int width, int height, SkRegion* inval);
+ void clear();
+ bool draw(SkCanvas* );
+ static PictureSet* GetNativePictureSet(JNIEnv* env, jobject jpic);
+ int height() const { return mHeight; }
+ bool isEmpty() const; // returns true if empty or only trivial content
+ bool reuseSubdivided(const SkRegion& );
+ void set(const PictureSet& );
+ void setDrawTimes(const PictureSet& );
+ void setPicture(size_t i, SkPicture* p);
+ size_t size() const { return mPictures.size(); }
+ void split(PictureSet* result) const;
+ bool upToDate(size_t i) const { return mPictures[i].mPicture != NULL; }
+ int width() const { return mWidth; }
+ void dump(const char* label) const;
+ bool validate(const char* label) const;
+ private:
+ bool emptyPicture(SkPicture* ) const; // true if no text, images, paths
+ struct Pictures {
+ SkRegion mArea;
+ SkPicture* mPicture;
+ SkIRect mUnsplit;
+ uint32_t mElapsed;
+ bool mSplit : 8;
+ bool mWroteElapsed : 8;
+ bool mBase : 8; // true if nothing is drawn underneath this
+ bool mEmpty : 8; // true if the picture only draws white
+ };
+ void add(const Pictures* temp);
+ WTF::Vector<Pictures> mPictures;
+ int mHeight;
+ int mWidth;
+ };
+}
+
+#endif
diff --git a/Source/WebKit/android/jni/WebCoreFrameBridge.cpp b/Source/WebKit/android/jni/WebCoreFrameBridge.cpp
new file mode 100644
index 0000000..15b6d20
--- /dev/null
+++ b/Source/WebKit/android/jni/WebCoreFrameBridge.cpp
@@ -0,0 +1,2125 @@
+/*
+ * 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 "WebCoreFrameBridge.h"
+
+#include "Arena.h"
+#include "BackForwardList.h"
+#include "MemoryCache.h"
+#include "Chrome.h"
+#include "ChromeClientAndroid.h"
+#include "ChromiumInit.h"
+#include "ContextMenuClientAndroid.h"
+#include "DeviceMotionClientAndroid.h"
+#include "DeviceOrientationClientAndroid.h"
+#include "Document.h"
+#include "DocumentLoader.h"
+#include "DragClientAndroid.h"
+#include "EditorClientAndroid.h"
+#include "Element.h"
+#include "FocusController.h"
+#include "Font.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameLoaderClientAndroid.h"
+#include "FrameLoadRequest.h"
+#include "FrameTree.h"
+#include "FrameView.h"
+#include "GraphicsContext.h"
+#include "HistoryItem.h"
+#include "HTMLCollection.h"
+#include "HTMLElement.h"
+#include "HTMLFormElement.h"
+#include "HTMLInputElement.h"
+#include "HTMLNames.h"
+#include "IconDatabase.h"
+#include "Image.h"
+#include "InspectorClientAndroid.h"
+#include "KURL.h"
+#include "Page.h"
+#include "PageCache.h"
+#include "PlatformString.h"
+#include "RenderPart.h"
+#include "RenderSkinAndroid.h"
+#include "RenderTreeAsText.h"
+#include "RenderView.h"
+#include "ResourceHandle.h"
+#include "ResourceHandleInternal.h"
+#include "ScriptController.h"
+#include "ScriptValue.h"
+#include "SecurityOrigin.h"
+#include "SelectionController.h"
+#include "Settings.h"
+#include "SubstituteData.h"
+#include "UrlInterceptResponse.h"
+#include "UserGestureIndicator.h"
+#include "WebCache.h"
+#include "WebCoreJni.h"
+#include "WebCoreResourceLoader.h"
+#include "WebHistory.h"
+#include "WebIconDatabase.h"
+#include "WebFrameView.h"
+#include "WebUrlLoaderClient.h"
+#include "WebViewCore.h"
+#include "android_graphics.h"
+#include "jni.h"
+#include "wds/DebugServer.h"
+
+#include <JNIUtility.h>
+#include <JNIHelp.h>
+#include <SkGraphics.h>
+#include <android_runtime/android_util_AssetManager.h>
+#include <utils/misc.h>
+#include <utils/AssetManager.h>
+#include <wtf/CurrentTime.h>
+#include <wtf/Platform.h>
+#include <wtf/text/AtomicString.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/StringBuilder.h>
+
+#if USE(JSC)
+#include "GCController.h"
+#include "JSDOMWindow.h"
+#include "JavaInstanceJSC.h"
+#include <runtime_object.h>
+#include <runtime_root.h>
+#include <runtime/JSLock.h>
+#elif USE(V8)
+#include "JavaNPObjectV8.h"
+#include "JavaInstanceV8.h"
+#include "V8Counters.h"
+#endif // USE(JSC)
+
+#ifdef ANDROID_INSTRUMENT
+#include "TimeCounter.h"
+#endif
+
+#if ENABLE(ARCHIVE)
+#include "WebArchiveAndroid.h"
+#endif
+
+#if ENABLE(WEB_AUTOFILL)
+#include "autofill/WebAutoFill.h"
+#endif
+
+using namespace JSC::Bindings;
+
+static String* gUploadFileLabel;
+static String* gResetLabel;
+static String* gSubmitLabel;
+static String* gNoFileChosenLabel;
+
+String* WebCore::PlatformBridge::globalLocalizedName(
+ WebCore::PlatformBridge::rawResId resId)
+{
+ switch (resId) {
+ case WebCore::PlatformBridge::FileUploadLabel:
+ return gUploadFileLabel;
+ case WebCore::PlatformBridge::ResetLabel:
+ return gResetLabel;
+ case WebCore::PlatformBridge::SubmitLabel:
+ return gSubmitLabel;
+ case WebCore::PlatformBridge::FileUploadNoFileChosenLabel:
+ return gNoFileChosenLabel;
+
+ default:
+ return 0;
+ }
+}
+/**
+ * Instantiate the localized name desired.
+ */
+void initGlobalLocalizedName(WebCore::PlatformBridge::rawResId resId,
+ android::WebFrame* webFrame)
+{
+ String** pointer;
+ switch (resId) {
+ case WebCore::PlatformBridge::FileUploadLabel:
+ pointer = &gUploadFileLabel;
+ break;
+ case WebCore::PlatformBridge::ResetLabel:
+ pointer = &gResetLabel;
+ break;
+ case WebCore::PlatformBridge::SubmitLabel:
+ pointer = &gSubmitLabel;
+ break;
+ case WebCore::PlatformBridge::FileUploadNoFileChosenLabel:
+ pointer = &gNoFileChosenLabel;
+ break;
+ default:
+ return;
+ }
+ if (!(*pointer) && webFrame) {
+ (*pointer) = new String(webFrame->getRawResourceFilename(resId).impl());
+ }
+}
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+#define WEBCORE_MEMORY_CAP 15 * 1024 * 1024
+
+// ----------------------------------------------------------------------------
+
+struct WebFrame::JavaBrowserFrame
+{
+ jweak mObj;
+ jweak mHistoryList; // WebBackForwardList object
+ jmethodID mStartLoadingResource;
+ jmethodID mMaybeSavePassword;
+ jmethodID mShouldInterceptRequest;
+ jmethodID mLoadStarted;
+ jmethodID mTransitionToCommitted;
+ jmethodID mLoadFinished;
+ jmethodID mReportError;
+ jmethodID mSetTitle;
+ jmethodID mWindowObjectCleared;
+ jmethodID mSetProgress;
+ jmethodID mDidReceiveIcon;
+ jmethodID mDidReceiveTouchIconUrl;
+ jmethodID mUpdateVisitedHistory;
+ jmethodID mHandleUrl;
+ jmethodID mCreateWindow;
+ jmethodID mCloseWindow;
+ jmethodID mDecidePolicyForFormResubmission;
+ jmethodID mRequestFocus;
+ jmethodID mGetRawResFilename;
+ jmethodID mDensity;
+ jmethodID mGetFileSize;
+ jmethodID mGetFile;
+ jmethodID mDidReceiveAuthenticationChallenge;
+ jmethodID mReportSslCertError;
+ jmethodID mDownloadStart;
+ jmethodID mDidReceiveData;
+ jmethodID mDidFinishLoading;
+ jmethodID mSetCertificate;
+ jmethodID mShouldSaveFormData;
+ jmethodID mSaveFormData;
+ jmethodID mAutoLogin;
+ AutoJObject frame(JNIEnv* env) {
+ return getRealObject(env, mObj);
+ }
+ AutoJObject history(JNIEnv* env) {
+ return getRealObject(env, mHistoryList);
+ }
+};
+
+static jfieldID gFrameField;
+#define GET_NATIVE_FRAME(env, obj) ((WebCore::Frame*)env->GetIntField(obj, gFrameField))
+#define SET_NATIVE_FRAME(env, obj, frame) (env->SetIntField(obj, gFrameField, frame))
+
+// ----------------------------------------------------------------------------
+
+WebFrame::WebFrame(JNIEnv* env, jobject obj, jobject historyList, WebCore::Page* page)
+ : mPage(page)
+{
+ jclass clazz = env->GetObjectClass(obj);
+ mJavaFrame = new JavaBrowserFrame;
+ mJavaFrame->mObj = env->NewWeakGlobalRef(obj);
+ mJavaFrame->mHistoryList = env->NewWeakGlobalRef(historyList);
+ mJavaFrame->mStartLoadingResource = env->GetMethodID(clazz, "startLoadingResource",
+ "(ILjava/lang/String;Ljava/lang/String;Ljava/util/HashMap;[BJIZZZLjava/lang/String;Ljava/lang/String;)Landroid/webkit/LoadListener;");
+ mJavaFrame->mMaybeSavePassword = env->GetMethodID(clazz, "maybeSavePassword",
+ "([BLjava/lang/String;Ljava/lang/String;)V");
+ mJavaFrame->mShouldInterceptRequest =
+ env->GetMethodID(clazz, "shouldInterceptRequest",
+ "(Ljava/lang/String;)Landroid/webkit/WebResourceResponse;");
+ mJavaFrame->mLoadStarted = env->GetMethodID(clazz, "loadStarted",
+ "(Ljava/lang/String;Landroid/graphics/Bitmap;IZ)V");
+ mJavaFrame->mTransitionToCommitted = env->GetMethodID(clazz, "transitionToCommitted",
+ "(IZ)V");
+ mJavaFrame->mLoadFinished = env->GetMethodID(clazz, "loadFinished",
+ "(Ljava/lang/String;IZ)V");
+ mJavaFrame->mReportError = env->GetMethodID(clazz, "reportError",
+ "(ILjava/lang/String;Ljava/lang/String;)V");
+ mJavaFrame->mSetTitle = env->GetMethodID(clazz, "setTitle",
+ "(Ljava/lang/String;)V");
+ mJavaFrame->mWindowObjectCleared = env->GetMethodID(clazz, "windowObjectCleared",
+ "(I)V");
+ mJavaFrame->mSetProgress = env->GetMethodID(clazz, "setProgress",
+ "(I)V");
+ mJavaFrame->mDidReceiveIcon = env->GetMethodID(clazz, "didReceiveIcon",
+ "(Landroid/graphics/Bitmap;)V");
+ mJavaFrame->mDidReceiveTouchIconUrl = env->GetMethodID(clazz, "didReceiveTouchIconUrl",
+ "(Ljava/lang/String;Z)V");
+ mJavaFrame->mUpdateVisitedHistory = env->GetMethodID(clazz, "updateVisitedHistory",
+ "(Ljava/lang/String;Z)V");
+ mJavaFrame->mHandleUrl = env->GetMethodID(clazz, "handleUrl",
+ "(Ljava/lang/String;)Z");
+ mJavaFrame->mCreateWindow = env->GetMethodID(clazz, "createWindow",
+ "(ZZ)Landroid/webkit/BrowserFrame;");
+ mJavaFrame->mCloseWindow = env->GetMethodID(clazz, "closeWindow",
+ "(Landroid/webkit/WebViewCore;)V");
+ mJavaFrame->mDecidePolicyForFormResubmission = env->GetMethodID(clazz,
+ "decidePolicyForFormResubmission", "(I)V");
+ mJavaFrame->mRequestFocus = env->GetMethodID(clazz, "requestFocus",
+ "()V");
+ mJavaFrame->mGetRawResFilename = env->GetMethodID(clazz, "getRawResFilename",
+ "(I)Ljava/lang/String;");
+ mJavaFrame->mDensity = env->GetMethodID(clazz, "density","()F");
+ mJavaFrame->mGetFileSize = env->GetMethodID(clazz, "getFileSize", "(Ljava/lang/String;)I");
+ mJavaFrame->mGetFile = env->GetMethodID(clazz, "getFile", "(Ljava/lang/String;[BII)I");
+ mJavaFrame->mDidReceiveAuthenticationChallenge = env->GetMethodID(clazz, "didReceiveAuthenticationChallenge",
+ "(ILjava/lang/String;Ljava/lang/String;Z)V");
+ mJavaFrame->mReportSslCertError = env->GetMethodID(clazz, "reportSslCertError", "(II[B)V");
+ mJavaFrame->mDownloadStart = env->GetMethodID(clazz, "downloadStart",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;J)V");
+ mJavaFrame->mDidReceiveData = env->GetMethodID(clazz, "didReceiveData", "([BI)V");
+ mJavaFrame->mDidFinishLoading = env->GetMethodID(clazz, "didFinishLoading", "()V");
+ mJavaFrame->mSetCertificate = env->GetMethodID(clazz, "setCertificate", "([B)V");
+ mJavaFrame->mShouldSaveFormData = env->GetMethodID(clazz, "shouldSaveFormData", "()Z");
+ mJavaFrame->mSaveFormData = env->GetMethodID(clazz, "saveFormData", "(Ljava/util/HashMap;)V");
+ mJavaFrame->mAutoLogin = env->GetMethodID(clazz, "autoLogin",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
+ env->DeleteLocalRef(clazz);
+
+ LOG_ASSERT(mJavaFrame->mStartLoadingResource, "Could not find method startLoadingResource");
+ LOG_ASSERT(mJavaFrame->mMaybeSavePassword, "Could not find method maybeSavePassword");
+ LOG_ASSERT(mJavaFrame->mShouldInterceptRequest, "Could not find method shouldInterceptRequest");
+ LOG_ASSERT(mJavaFrame->mLoadStarted, "Could not find method loadStarted");
+ LOG_ASSERT(mJavaFrame->mTransitionToCommitted, "Could not find method transitionToCommitted");
+ LOG_ASSERT(mJavaFrame->mLoadFinished, "Could not find method loadFinished");
+ LOG_ASSERT(mJavaFrame->mReportError, "Could not find method reportError");
+ LOG_ASSERT(mJavaFrame->mSetTitle, "Could not find method setTitle");
+ LOG_ASSERT(mJavaFrame->mWindowObjectCleared, "Could not find method windowObjectCleared");
+ LOG_ASSERT(mJavaFrame->mSetProgress, "Could not find method setProgress");
+ LOG_ASSERT(mJavaFrame->mDidReceiveIcon, "Could not find method didReceiveIcon");
+ LOG_ASSERT(mJavaFrame->mDidReceiveTouchIconUrl, "Could not find method didReceiveTouchIconUrl");
+ LOG_ASSERT(mJavaFrame->mUpdateVisitedHistory, "Could not find method updateVisitedHistory");
+ LOG_ASSERT(mJavaFrame->mHandleUrl, "Could not find method handleUrl");
+ LOG_ASSERT(mJavaFrame->mCreateWindow, "Could not find method createWindow");
+ LOG_ASSERT(mJavaFrame->mCloseWindow, "Could not find method closeWindow");
+ LOG_ASSERT(mJavaFrame->mDecidePolicyForFormResubmission, "Could not find method decidePolicyForFormResubmission");
+ LOG_ASSERT(mJavaFrame->mRequestFocus, "Could not find method requestFocus");
+ LOG_ASSERT(mJavaFrame->mGetRawResFilename, "Could not find method getRawResFilename");
+ LOG_ASSERT(mJavaFrame->mDensity, "Could not find method density");
+ LOG_ASSERT(mJavaFrame->mGetFileSize, "Could not find method getFileSize");
+ LOG_ASSERT(mJavaFrame->mGetFile, "Could not find method getFile");
+ LOG_ASSERT(mJavaFrame->mDidReceiveAuthenticationChallenge, "Could not find method didReceiveAuthenticationChallenge");
+ LOG_ASSERT(mJavaFrame->mReportSslCertError, "Could not find method reportSslCertError");
+ LOG_ASSERT(mJavaFrame->mDownloadStart, "Could not find method downloadStart");
+ LOG_ASSERT(mJavaFrame->mDidReceiveData, "Could not find method didReceiveData");
+ LOG_ASSERT(mJavaFrame->mDidFinishLoading, "Could not find method didFinishLoading");
+ LOG_ASSERT(mJavaFrame->mSetCertificate, "Could not find method setCertificate");
+ LOG_ASSERT(mJavaFrame->mShouldSaveFormData, "Could not find method shouldSaveFormData");
+ LOG_ASSERT(mJavaFrame->mSaveFormData, "Could not find method saveFormData");
+ LOG_ASSERT(mJavaFrame->mAutoLogin, "Could not find method autoLogin");
+
+ mUserAgent = WTF::String();
+ mUserInitiatedAction = false;
+ mBlockNetworkLoads = false;
+ m_renderSkins = 0;
+}
+
+WebFrame::~WebFrame()
+{
+ if (mJavaFrame->mObj) {
+ JNIEnv* env = getJNIEnv();
+ env->DeleteWeakGlobalRef(mJavaFrame->mObj);
+ env->DeleteWeakGlobalRef(mJavaFrame->mHistoryList);
+ mJavaFrame->mObj = 0;
+ }
+ delete mJavaFrame;
+ delete m_renderSkins;
+}
+
+WebFrame* WebFrame::getWebFrame(const WebCore::Frame* frame)
+{
+ FrameLoaderClientAndroid* client =
+ static_cast<FrameLoaderClientAndroid*> (frame->loader()->client());
+ return client->webFrame();
+}
+
+static jobject createJavaMapFromHTTPHeaders(JNIEnv* env, const WebCore::HTTPHeaderMap& map)
+{
+ jclass mapClass = env->FindClass("java/util/HashMap");
+ LOG_ASSERT(mapClass, "Could not find HashMap class!");
+ jmethodID init = env->GetMethodID(mapClass, "<init>", "(I)V");
+ LOG_ASSERT(init, "Could not find constructor for HashMap");
+ jobject hashMap = env->NewObject(mapClass, init, map.size());
+ LOG_ASSERT(hashMap, "Could not create a new HashMap");
+ jmethodID put = env->GetMethodID(mapClass, "put",
+ "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+ LOG_ASSERT(put, "Could not find put method on HashMap");
+
+ WebCore::HTTPHeaderMap::const_iterator end = map.end();
+ for (WebCore::HTTPHeaderMap::const_iterator i = map.begin(); i != end; ++i) {
+ if (i->first.length() == 0 || i->second.length() == 0)
+ continue;
+ jstring key = wtfStringToJstring(env, i->first);
+ jstring val = wtfStringToJstring(env, i->second);
+ if (key && val) {
+ env->CallObjectMethod(hashMap, put, key, val);
+ }
+ env->DeleteLocalRef(key);
+ env->DeleteLocalRef(val);
+ }
+
+ env->DeleteLocalRef(mapClass);
+
+ return hashMap;
+}
+
+// This class stores the URI and the size of each file for upload. The URI is
+// stored so we do not have to create it again. The size is stored so we can
+// compare the actual size of the file with the stated size. If the actual size
+// is larger, we will not copy it, since we will not have enough space in our
+// buffer.
+class FileInfo {
+public:
+ FileInfo(JNIEnv* env, const WTF::String& name) {
+ m_uri = wtfStringToJstring(env, name);
+ checkException(env);
+ m_size = 0;
+ m_env = env;
+ }
+ ~FileInfo() {
+ m_env->DeleteLocalRef(m_uri);
+ }
+ int getSize() { return m_size; }
+ jstring getUri() { return m_uri; }
+ void setSize(int size) { m_size = size; }
+private:
+ // This is only a pointer to the JNIEnv* returned by
+ // JSC::Bindings::getJNIEnv(). Used to delete the jstring when finished.
+ JNIEnv* m_env;
+ jstring m_uri;
+ int m_size;
+};
+
+PassRefPtr<WebCore::ResourceLoaderAndroid>
+WebFrame::startLoadingResource(WebCore::ResourceHandle* loader,
+ const WebCore::ResourceRequest& request,
+ bool mainResource,
+ bool synchronous)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ LOGV("::WebCore:: startLoadingResource(%p, %s)",
+ loader, request.url().string().latin1().data());
+
+ WTF::String method = request.httpMethod();
+ WebCore::HTTPHeaderMap headers = request.httpHeaderFields();
+
+ JNIEnv* env = getJNIEnv();
+ WTF::String urlStr = request.url().string();
+ int colon = urlStr.find(':');
+ bool allLower = true;
+ for (int index = 0; index < colon; index++) {
+ UChar ch = urlStr[index];
+ if (!WTF::isASCIIAlpha(ch))
+ break;
+ allLower &= WTF::isASCIILower(ch);
+ if (index == colon - 1 && !allLower) {
+ urlStr = urlStr.substring(0, colon).lower()
+ + urlStr.substring(colon);
+ }
+ }
+ LOGV("%s lower=%s", __FUNCTION__, urlStr.latin1().data());
+ jstring jUrlStr = wtfStringToJstring(env, urlStr);
+ jstring jMethodStr = NULL;
+ if (!method.isEmpty())
+ jMethodStr = wtfStringToJstring(env, method);
+ WebCore::FormData* formdata = request.httpBody();
+ jbyteArray jPostDataStr = getPostData(request);
+ jobject jHeaderMap = createJavaMapFromHTTPHeaders(env, headers);
+
+ // Convert the WebCore Cache Policy to a WebView Cache Policy.
+ int cacheMode = 0; // WebSettings.LOAD_NORMAL
+ switch (request.cachePolicy()) {
+ case WebCore::ReloadIgnoringCacheData:
+ cacheMode = 2; // WebSettings.LOAD_NO_CACHE
+ break;
+ case WebCore::ReturnCacheDataDontLoad:
+ cacheMode = 3; // WebSettings.LOAD_CACHE_ONLY
+ break;
+ case WebCore::ReturnCacheDataElseLoad:
+ cacheMode = 1; // WebSettings.LOAD_CACHE_ELSE_NETWORK
+ break;
+ case WebCore::UseProtocolCachePolicy:
+ default:
+ break;
+ }
+
+ LOGV("::WebCore:: startLoadingResource %s with cacheMode %d", urlStr.ascii().data(), cacheMode);
+
+ ResourceHandleInternal* loaderInternal = loader->getInternal();
+ jstring jUsernameString = loaderInternal->m_user.isEmpty() ?
+ NULL : wtfStringToJstring(env, loaderInternal->m_user);
+ jstring jPasswordString = loaderInternal->m_pass.isEmpty() ?
+ NULL : wtfStringToJstring(env, loaderInternal->m_pass);
+
+ bool isUserGesture = UserGestureIndicator::processingUserGesture();
+ jobject jLoadListener =
+ env->CallObjectMethod(mJavaFrame->frame(env).get(), mJavaFrame->mStartLoadingResource,
+ (int)loader, jUrlStr, jMethodStr, jHeaderMap,
+ jPostDataStr, formdata ? formdata->identifier(): 0,
+ cacheMode, mainResource, isUserGesture,
+ synchronous, jUsernameString, jPasswordString);
+
+ env->DeleteLocalRef(jUrlStr);
+ env->DeleteLocalRef(jMethodStr);
+ env->DeleteLocalRef(jPostDataStr);
+ env->DeleteLocalRef(jHeaderMap);
+ env->DeleteLocalRef(jUsernameString);
+ env->DeleteLocalRef(jPasswordString);
+ if (checkException(env))
+ return NULL;
+
+ PassRefPtr<WebCore::ResourceLoaderAndroid> h;
+ if (jLoadListener)
+ h = WebCoreResourceLoader::create(env, jLoadListener);
+ env->DeleteLocalRef(jLoadListener);
+ return h;
+}
+
+UrlInterceptResponse*
+WebFrame::shouldInterceptRequest(const WTF::String& url)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ LOGV("::WebCore:: shouldInterceptRequest(%s)", url.latin1().data());
+
+ JNIEnv* env = getJNIEnv();
+ jstring urlStr = wtfStringToJstring(env, url);
+ jobject response = env->CallObjectMethod(mJavaFrame->frame(env).get(), mJavaFrame->mShouldInterceptRequest, urlStr);
+ env->DeleteLocalRef(urlStr);
+ if (response == 0)
+ return 0;
+ return new UrlInterceptResponse(env, response);
+}
+
+void
+WebFrame::reportError(int errorCode, const WTF::String& description,
+ const WTF::String& failingUrl)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ LOGV("::WebCore:: reportError(%d, %s)", errorCode, description.ascii().data());
+ JNIEnv* env = getJNIEnv();
+
+ jstring descStr = wtfStringToJstring(env, description);
+ jstring failUrl = wtfStringToJstring(env, failingUrl);
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mReportError,
+ errorCode, descStr, failUrl);
+ env->DeleteLocalRef(descStr);
+ env->DeleteLocalRef(failUrl);
+}
+
+void
+WebFrame::loadStarted(WebCore::Frame* frame)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ // activeDocumentLoader() can return null.
+ DocumentLoader* documentLoader = frame->loader()->activeDocumentLoader();
+ if (documentLoader == NULL)
+ return;
+
+ const WebCore::KURL& url = documentLoader->url();
+ if (url.isEmpty())
+ return;
+ LOGV("::WebCore:: loadStarted %s", url.string().ascii().data());
+
+ bool isMainFrame = (!frame->tree() || !frame->tree()->parent());
+ WebCore::FrameLoadType loadType = frame->loader()->loadType();
+
+ if (loadType == WebCore::FrameLoadTypeReplace ||
+ (loadType == WebCore::FrameLoadTypeRedirectWithLockedBackForwardList &&
+ !isMainFrame))
+ return;
+
+ JNIEnv* env = getJNIEnv();
+ const WTF::String& urlString = url.string();
+ // If this is the main frame and we already have a favicon in the database,
+ // send it along with the page started notification.
+ jobject favicon = NULL;
+ if (isMainFrame) {
+ WebCore::Image* icon = WebCore::iconDatabase()->iconForPageURL(urlString, WebCore::IntSize(16, 16));
+ if (icon)
+ favicon = webcoreImageToJavaBitmap(env, icon);
+ LOGV("favicons", "Starting load with icon %p for %s", icon, url.string().utf8().data());
+ }
+ jstring urlStr = wtfStringToJstring(env, urlString);
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mLoadStarted, urlStr, favicon,
+ (int)loadType, isMainFrame);
+ checkException(env);
+ env->DeleteLocalRef(urlStr);
+ if (favicon)
+ env->DeleteLocalRef(favicon);
+
+ // Inform the client that the main frame has started a new load.
+ if (isMainFrame && mPage) {
+ Chrome* chrome = mPage->chrome();
+ if (chrome) {
+ ChromeClientAndroid* client = static_cast<ChromeClientAndroid*>(chrome->client());
+ if (client)
+ client->onMainFrameLoadStarted();
+ }
+ }
+}
+
+void
+WebFrame::transitionToCommitted(WebCore::Frame* frame)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ JNIEnv* env = getJNIEnv();
+ WebCore::FrameLoadType loadType = frame->loader()->loadType();
+ bool isMainFrame = (!frame->tree() || !frame->tree()->parent());
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mTransitionToCommitted,
+ (int)loadType, isMainFrame);
+ checkException(env);
+}
+
+void
+WebFrame::didFinishLoad(WebCore::Frame* frame)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ JNIEnv* env = getJNIEnv();
+
+ // activeDocumentLoader() can return null.
+ WebCore::FrameLoader* loader = frame->loader();
+ DocumentLoader* documentLoader = loader->activeDocumentLoader();
+ if (documentLoader == NULL)
+ return;
+
+ const WebCore::KURL& url = documentLoader->url();
+ if (url.isEmpty())
+ return;
+ LOGV("::WebCore:: didFinishLoad %s", url.string().ascii().data());
+
+ bool isMainFrame = (!frame->tree() || !frame->tree()->parent());
+ WebCore::FrameLoadType loadType = loader->loadType();
+ const WTF::String& urlString = url.string();
+ jstring urlStr = wtfStringToJstring(env, urlString);
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mLoadFinished, urlStr,
+ (int)loadType, isMainFrame);
+ checkException(env);
+ env->DeleteLocalRef(urlStr);
+}
+
+void
+WebFrame::addHistoryItem(WebCore::HistoryItem* item)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ LOGV("::WebCore:: addHistoryItem");
+ JNIEnv* env = getJNIEnv();
+ WebHistory::AddItem(mJavaFrame->history(env), item);
+}
+
+void
+WebFrame::removeHistoryItem(int index)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ LOGV("::WebCore:: removeHistoryItem at %d", index);
+ JNIEnv* env = getJNIEnv();
+ WebHistory::RemoveItem(mJavaFrame->history(env), index);
+}
+
+void
+WebFrame::updateHistoryIndex(int newIndex)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ LOGV("::WebCore:: updateHistoryIndex to %d", newIndex);
+ JNIEnv* env = getJNIEnv();
+ WebHistory::UpdateHistoryIndex(mJavaFrame->history(env), newIndex);
+}
+
+void
+WebFrame::setTitle(const WTF::String& title)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+#ifndef NDEBUG
+ LOGV("setTitle(%s)", title.ascii().data());
+#endif
+ JNIEnv* env = getJNIEnv();
+ jstring jTitleStr = wtfStringToJstring(env, title);
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mSetTitle, jTitleStr);
+ checkException(env);
+ env->DeleteLocalRef(jTitleStr);
+}
+
+void
+WebFrame::windowObjectCleared(WebCore::Frame* frame)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ LOGV("::WebCore:: windowObjectCleared");
+ JNIEnv* env = getJNIEnv();
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mWindowObjectCleared, (int)frame);
+ checkException(env);
+}
+
+void
+WebFrame::setProgress(float newProgress)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ JNIEnv* env = getJNIEnv();
+ int progress = (int) (100 * newProgress);
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mSetProgress, progress);
+ checkException(env);
+}
+
+const WTF::String
+WebFrame::userAgentForURL(const WebCore::KURL* url)
+{
+ return mUserAgent;
+}
+
+void
+WebFrame::didReceiveIcon(WebCore::Image* icon)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ LOG_ASSERT(icon, "DidReceiveIcon called without an image!");
+ JNIEnv* env = getJNIEnv();
+ jobject bitmap = webcoreImageToJavaBitmap(env, icon);
+ if (!bitmap)
+ return;
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mDidReceiveIcon, bitmap);
+ env->DeleteLocalRef(bitmap);
+ checkException(env);
+}
+
+void
+WebFrame::didReceiveTouchIconURL(const WTF::String& url, bool precomposed)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ JNIEnv* env = getJNIEnv();
+ jstring jUrlStr = wtfStringToJstring(env, url);
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(),
+ mJavaFrame->mDidReceiveTouchIconUrl, jUrlStr, precomposed);
+ env->DeleteLocalRef(jUrlStr);
+ checkException(env);
+}
+
+void
+WebFrame::updateVisitedHistory(const WebCore::KURL& url, bool reload)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ const WTF::String& urlStr = url.string();
+ JNIEnv* env = getJNIEnv();
+ jstring jUrlStr = wtfStringToJstring(env, urlStr);
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mUpdateVisitedHistory, jUrlStr, reload);
+ env->DeleteLocalRef(jUrlStr);
+ checkException(env);
+}
+
+bool
+WebFrame::canHandleRequest(const WebCore::ResourceRequest& request)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ // always handle "POST" in place
+ if (equalIgnoringCase(request.httpMethod(), "POST"))
+ return true;
+ const WebCore::KURL& requestUrl = request.url();
+ bool isUserGesture = UserGestureIndicator::processingUserGesture();
+ if (!mUserInitiatedAction && !isUserGesture &&
+ (requestUrl.protocolIs("http") || requestUrl.protocolIs("https") ||
+ requestUrl.protocolIs("file") || requestUrl.protocolIs("about") ||
+ WebCore::protocolIsJavaScript(requestUrl.string())))
+ return true;
+ const WTF::String& url = requestUrl.string();
+ // Empty urls should not be sent to java
+ if (url.isEmpty())
+ return true;
+ JNIEnv* env = getJNIEnv();
+ jstring jUrlStr = wtfStringToJstring(env, url);
+
+ // check to see whether browser app wants to hijack url loading.
+ // if browser app handles the url, we will return false to bail out WebCore loading
+ jboolean ret = env->CallBooleanMethod(mJavaFrame->frame(env).get(), mJavaFrame->mHandleUrl, jUrlStr);
+ checkException(env);
+ env->DeleteLocalRef(jUrlStr);
+ return (ret == 0);
+}
+
+bool
+WebFrame::shouldSaveFormData()
+{
+ JNIEnv* env = getJNIEnv();
+ jboolean ret = env->CallBooleanMethod(mJavaFrame->frame(env).get(),
+ mJavaFrame->mShouldSaveFormData);
+ checkException(env);
+ return ret;
+}
+
+WebCore::Frame*
+WebFrame::createWindow(bool dialog, bool userGesture)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ JNIEnv* env = getJNIEnv();
+ jobject obj = env->CallObjectMethod(mJavaFrame->frame(env).get(),
+ mJavaFrame->mCreateWindow, dialog, userGesture);
+ if (obj) {
+ WebCore::Frame* frame = GET_NATIVE_FRAME(env, obj);
+ return frame;
+ }
+ return NULL;
+}
+
+void
+WebFrame::requestFocus() const
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ JNIEnv* env = getJNIEnv();
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mRequestFocus);
+ checkException(env);
+}
+
+void
+WebFrame::closeWindow(WebViewCore* webViewCore)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ assert(webViewCore);
+ JNIEnv* env = getJNIEnv();
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mCloseWindow,
+ webViewCore->getJavaObject().get());
+}
+
+struct PolicyFunctionWrapper {
+ WebCore::FramePolicyFunction func;
+};
+
+void
+WebFrame::decidePolicyForFormResubmission(WebCore::FramePolicyFunction func)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ JNIEnv* env = getJNIEnv();
+ PolicyFunctionWrapper* p = new PolicyFunctionWrapper;
+ p->func = func;
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mDecidePolicyForFormResubmission, p);
+}
+
+WTF::String
+WebFrame::getRawResourceFilename(WebCore::PlatformBridge::rawResId id) const
+{
+ JNIEnv* env = getJNIEnv();
+ jstring ret = (jstring) env->CallObjectMethod(mJavaFrame->frame(env).get(),
+ mJavaFrame->mGetRawResFilename, (int)id);
+
+ return jstringToWtfString(env, ret);
+}
+
+float
+WebFrame::density() const
+{
+ JNIEnv* env = getJNIEnv();
+ jfloat dpi = env->CallFloatMethod(mJavaFrame->frame(env).get(), mJavaFrame->mDensity);
+ checkException(env);
+ return dpi;
+}
+
+#if USE(CHROME_NETWORK_STACK)
+void
+WebFrame::didReceiveAuthenticationChallenge(WebUrlLoaderClient* client, const std::string& host, const std::string& realm, bool useCachedCredentials)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ JNIEnv* env = getJNIEnv();
+ int jHandle = reinterpret_cast<int>(client);
+ jstring jHost = stdStringToJstring(env, host, true);
+ jstring jRealm = stdStringToJstring(env, realm, true);
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(),
+ mJavaFrame->mDidReceiveAuthenticationChallenge, jHandle, jHost, jRealm, useCachedCredentials);
+ env->DeleteLocalRef(jHost);
+ env->DeleteLocalRef(jRealm);
+ checkException(env);
+}
+#endif
+
+void
+WebFrame::reportSslCertError(WebUrlLoaderClient* client, int cert_error, const std::string& cert)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ JNIEnv* env = getJNIEnv();
+ int jHandle = reinterpret_cast<int>(client);
+
+ int len = cert.length();
+ jbyteArray jCert = env->NewByteArray(len);
+ jbyte* bytes = env->GetByteArrayElements(jCert, NULL);
+ cert.copy(reinterpret_cast<char*>(bytes), len);
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(),
+ mJavaFrame->mReportSslCertError, jHandle, cert_error, jCert);
+ env->DeleteLocalRef(jCert);
+ checkException(env);
+}
+
+#if USE(CHROME_NETWORK_STACK)
+void
+WebFrame::downloadStart(const std::string& url, const std::string& userAgent, const std::string& contentDisposition, const std::string& mimetype, long long contentLength)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ JNIEnv* env = getJNIEnv();
+ jstring jUrl = stdStringToJstring(env, url, true);
+ jstring jUserAgent = stdStringToJstring(env, userAgent, true);
+ jstring jContentDisposition = stdStringToJstring(env, contentDisposition, true);
+ jstring jMimetype = stdStringToJstring(env, mimetype, true);
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(),
+ mJavaFrame->mDownloadStart, jUrl, jUserAgent, jContentDisposition, jMimetype, contentLength);
+
+ env->DeleteLocalRef(jUrl);
+ env->DeleteLocalRef(jUserAgent);
+ env->DeleteLocalRef(jContentDisposition);
+ env->DeleteLocalRef(jMimetype);
+ checkException(env);
+}
+
+void
+WebFrame::didReceiveData(const char* data, int size) {
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ JNIEnv* env = getJNIEnv();
+
+ jbyteArray jData = env->NewByteArray(size);
+ jbyte* bytes = env->GetByteArrayElements(jData, NULL);
+ memcpy(reinterpret_cast<char*>(bytes), data, size);
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(),
+ mJavaFrame->mDidReceiveData, jData, size);
+ env->DeleteLocalRef(jData);
+ checkException(env);
+}
+
+void
+WebFrame::didFinishLoading() {
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ JNIEnv* env = getJNIEnv();
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mDidFinishLoading);
+ checkException(env);
+}
+
+#endif
+
+#if USE(CHROME_NETWORK_STACK)
+void WebFrame::setCertificate(const std::string& cert)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::JavaCallbackTimeCounter);
+#endif
+ JNIEnv* env = getJNIEnv();
+
+ int len = cert.length();
+ jbyteArray jCert = env->NewByteArray(len);
+ jbyte* bytes = env->GetByteArrayElements(jCert, NULL);
+ cert.copy(reinterpret_cast<char*>(bytes), len);
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mSetCertificate, jCert);
+
+ env->DeleteLocalRef(jCert);
+ checkException(env);
+}
+#endif
+
+void WebFrame::autoLogin(const std::string& loginHeader)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimerCoutner::JavaCallbackTimeCounter);
+#endif
+ WTF::String header(loginHeader.c_str(), loginHeader.length());
+ WTF::Vector<WTF::String> split;
+ header.split('&', split);
+ if (!split.isEmpty()) {
+ WTF::String realm;
+ WTF::String account;
+ WTF::String args;
+ int len = split.size();
+ while (len--) {
+ WTF::String& str = split[len];
+ size_t equals = str.find('=');
+ if (equals == WTF::notFound)
+ continue;
+
+ WTF::String* result = 0;
+ if (str.startsWith("realm", false))
+ result = &realm;
+ else if (str.startsWith("account", false))
+ result = &account;
+ else if (str.startsWith("args", false))
+ result = &args;
+
+ if (result)
+ // Decode url escape sequences before sending to the app.
+ *result = WebCore::decodeURLEscapeSequences(str.substring(equals + 1));
+ }
+
+ // realm and args are required parameters.
+ if (realm.isEmpty() || args.isEmpty())
+ return;
+
+ JNIEnv* env = getJNIEnv();
+ jstring jRealm = wtfStringToJstring(env, realm, true);
+ jstring jAccount = wtfStringToJstring(env, account);
+ jstring jArgs = wtfStringToJstring(env, args, true);
+ env->CallVoidMethod(mJavaFrame->frame(env).get(),
+ mJavaFrame->mAutoLogin, jRealm, jAccount, jArgs);
+ }
+}
+
+void WebFrame::maybeSavePassword(WebCore::Frame* frame, const WebCore::ResourceRequest& request)
+{
+ if (request.httpMethod() != "POST")
+ return;
+
+ WTF::String username;
+ WTF::String password;
+ if (!getUsernamePasswordFromDom(frame, username, password))
+ return;
+
+ JNIEnv* env = getJNIEnv();
+ jstring jUsername = wtfStringToJstring(env, username);
+ jstring jPassword = wtfStringToJstring(env, password);
+ jbyteArray jPostData = getPostData(request);
+ if (jPostData) {
+ env->CallVoidMethod(mJavaFrame->frame(env).get(),
+ mJavaFrame->mMaybeSavePassword, jPostData, jUsername, jPassword);
+ }
+
+ env->DeleteLocalRef(jPostData);
+ env->DeleteLocalRef(jUsername);
+ env->DeleteLocalRef(jPassword);
+ checkException(env);
+}
+
+bool WebFrame::getUsernamePasswordFromDom(WebCore::Frame* frame, WTF::String& username, WTF::String& password)
+{
+ bool found = false;
+ WTF::PassRefPtr<WebCore::HTMLCollection> form = frame->document()->forms();
+ WebCore::Node* node = form->firstItem();
+ while (node && !found && !node->namespaceURI().isNull() &&
+ !node->namespaceURI().isEmpty()) {
+ const WTF::Vector<WebCore::FormAssociatedElement*>& elements =
+ ((WebCore::HTMLFormElement*)node)->associatedElements();
+ size_t size = elements.size();
+ for (size_t i = 0; i< size && !found; i++) {
+ WebCore::HTMLElement* e = toHTMLElement(elements[i]);
+ if (e->hasLocalName(WebCore::HTMLNames::inputTag)) {
+ WebCore::HTMLInputElement* input = (WebCore::HTMLInputElement*)e;
+ if (input->autoComplete() == false)
+ continue;
+ if (input->isPasswordField())
+ password = input->value();
+ else if (input->isTextField() || input->isEmailField())
+ username = input->value();
+ if (!username.isNull() && !password.isNull())
+ found = true;
+ }
+ }
+ node = form->nextItem();
+ }
+ return found;
+}
+
+jbyteArray WebFrame::getPostData(const WebCore::ResourceRequest& request)
+{
+ jbyteArray jPostDataStr = NULL;
+ WebCore::FormData* formdata = request.httpBody();
+ if (formdata) {
+ JNIEnv* env = getJNIEnv();
+ AutoJObject obj = mJavaFrame->frame(env);
+
+ // We can use the formdata->flatten() but it will result in two
+ // memcpys, first through loading up the vector with the form data
+ // then another to copy it out of the vector and into the java byte
+ // array. Instead, we copy the form data ourselves below saving a
+ // memcpy.
+ const WTF::Vector<WebCore::FormDataElement>& elements =
+ formdata->elements();
+
+ // Sizing pass
+ int size = 0;
+ size_t n = elements.size();
+ FileInfo** fileinfos = new FileInfo*[n];
+ for (size_t i = 0; i < n; ++i) {
+ fileinfos[i] = 0;
+ const WebCore::FormDataElement& e = elements[i];
+ if (e.m_type == WebCore::FormDataElement::data) {
+ size += e.m_data.size();
+ } else if (e.m_type == WebCore::FormDataElement::encodedFile) {
+ fileinfos[i] = new FileInfo(env, e.m_filename);
+ int delta = env->CallIntMethod(obj.get(),
+ mJavaFrame->mGetFileSize, fileinfos[i]->getUri());
+ checkException(env);
+ fileinfos[i]->setSize(delta);
+ size += delta;
+ }
+ }
+
+ // Only create the byte array if there is POST data to pass up.
+ // The Java code is expecting null if there is no data.
+ if (size > 0) {
+ // Copy the actual form data.
+ jPostDataStr = env->NewByteArray(size);
+ if (jPostDataStr) {
+ // Write the form data to the java array.
+ jbyte* bytes = env->GetByteArrayElements(jPostDataStr, NULL);
+ int offset = 0;
+ for (size_t i = 0; i < n; ++i) {
+ const WebCore::FormDataElement& e = elements[i];
+ if (e.m_type == WebCore::FormDataElement::data) {
+ int delta = e.m_data.size();
+ memcpy(bytes + offset, e.m_data.data(), delta);
+ offset += delta;
+ } else if (e.m_type
+ == WebCore::FormDataElement::encodedFile) {
+ int delta = env->CallIntMethod(obj.get(),
+ mJavaFrame->mGetFile, fileinfos[i]->getUri(),
+ jPostDataStr, offset, fileinfos[i]->getSize());
+ checkException(env);
+ offset += delta;
+ }
+ }
+ env->ReleaseByteArrayElements(jPostDataStr, bytes, 0);
+ }
+ }
+ delete[] fileinfos;
+ }
+ return jPostDataStr;
+}
+
+// ----------------------------------------------------------------------------
+
+static void CallPolicyFunction(JNIEnv* env, jobject obj, jint func, jint decision)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ LOG_ASSERT(pFrame, "nativeCallPolicyFunction must take a valid frame pointer!");
+ PolicyFunctionWrapper* pFunc = (PolicyFunctionWrapper*)func;
+ LOG_ASSERT(pFunc, "nativeCallPolicyFunction must take a valid function pointer!");
+
+ // If we are resending the form then we should reset the multiple submission protection.
+ if (decision == WebCore::PolicyUse)
+ pFrame->loader()->resetMultipleFormSubmissionProtection();
+
+ (pFrame->loader()->policyChecker()->*(pFunc->func))((WebCore::PolicyAction)decision);
+}
+
+static void CreateFrame(JNIEnv* env, jobject obj, jobject javaview, jobject jAssetManager, jobject historyList)
+{
+ ScriptController::initializeThreading();
+
+#if USE(CHROME_NETWORK_STACK)
+ // needs to be called before any other chromium code
+ initChromium();
+#endif
+
+#ifdef ANDROID_INSTRUMENT
+#if USE(V8)
+ V8Counters::initCounters();
+#endif
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ // Create a new page
+ ChromeClientAndroid* chromeC = new ChromeClientAndroid;
+ EditorClientAndroid* editorC = new EditorClientAndroid;
+ DeviceMotionClientAndroid* deviceMotionC = new DeviceMotionClientAndroid;
+ DeviceOrientationClientAndroid* deviceOrientationC = new DeviceOrientationClientAndroid;
+
+ WebCore::Page::PageClients pageClients;
+ pageClients.chromeClient = chromeC;
+ pageClients.contextMenuClient = new ContextMenuClientAndroid;
+ pageClients.editorClient = editorC;
+ pageClients.dragClient = new DragClientAndroid;
+ pageClients.inspectorClient = new InspectorClientAndroid;
+ pageClients.deviceMotionClient = deviceMotionC;
+ pageClients.deviceOrientationClient = deviceOrientationC;
+ WebCore::Page* page = new WebCore::Page(pageClients);
+
+ editorC->setPage(page);
+ page->setGroupName("android.webkit");
+
+ // Create a WebFrame to access the Java BrowserFrame associated with this page
+ WebFrame* webFrame = new WebFrame(env, obj, historyList, page);
+ // Attach webFrame to pageClients.chromeClient and release our ownership
+ chromeC->setWebFrame(webFrame);
+ Release(webFrame);
+
+ FrameLoaderClientAndroid* loaderC = new FrameLoaderClientAndroid(webFrame);
+ // Create a Frame and the page holds its reference
+ WebCore::Frame* frame = WebCore::Frame::create(page, NULL, loaderC).get();
+ loaderC->setFrame(frame);
+#if ENABLE(WDS)
+ WDS::server()->addFrame(frame);
+#endif
+
+ // Create a WebViewCore to access the Java WebViewCore associated with this page
+ WebViewCore* webViewCore = new WebViewCore(env, javaview, frame);
+
+#if ENABLE(WEB_AUTOFILL)
+ editorC->getAutoFill()->setWebViewCore(webViewCore);
+#endif
+
+ // Create a FrameView
+ RefPtr<WebCore::FrameView> frameView = WebCore::FrameView::create(frame);
+ // Create a WebFrameView
+ WebFrameView* webFrameView = new WebFrameView(frameView.get(), webViewCore);
+ // As webFrameView Retains webViewCore, release our ownership
+ Release(webViewCore);
+ // As frameView Retains webFrameView, release our ownership
+ Release(webFrameView);
+ // Attach the frameView to the frame and release our ownership
+ frame->setView(frameView);
+ // Set the frame to active to turn on keyboard focus.
+ frame->init();
+ frame->selection()->setFocused(true);
+ frame->page()->focusController()->setFocused(true);
+ deviceMotionC->setWebViewCore(webViewCore);
+ deviceOrientationC->setWebViewCore(webViewCore);
+
+ // Allow local access to file:/// and substitute data
+ WebCore::SecurityOrigin::setLocalLoadPolicy(
+ WebCore::SecurityOrigin::AllowLocalLoadsForLocalAndSubstituteData);
+
+ LOGV("::WebCore:: createFrame %p", frame);
+
+ // Set the mNativeFrame field in Frame
+ SET_NATIVE_FRAME(env, obj, (int)frame);
+
+ String directory = webFrame->getRawResourceFilename(
+ WebCore::PlatformBridge::DrawableDir);
+ if (directory.isEmpty())
+ LOGE("Can't find the drawable directory");
+ else {
+ // Setup the asset manager.
+ AssetManager* am = assetManagerForJavaObject(env, jAssetManager);
+ // Initialize our skinning classes
+ webFrame->setRenderSkins(new WebCore::RenderSkinAndroid(am, directory));
+ }
+ for (int i = WebCore::PlatformBridge::FileUploadLabel;
+ i <= WebCore::PlatformBridge::FileUploadNoFileChosenLabel; i++)
+ initGlobalLocalizedName(
+ static_cast<WebCore::PlatformBridge::rawResId>(i), webFrame);
+}
+
+static void DestroyFrame(JNIEnv* env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ LOG_ASSERT(pFrame, "nativeDestroyFrame must take a valid frame pointer!");
+
+ LOGV("::WebCore:: deleting frame %p", pFrame);
+
+ WebCore::FrameView* view = pFrame->view();
+ view->ref();
+ // detachFromParent will cause the page to be closed.
+ WebCore::FrameLoader* fl = pFrame->loader();
+ // retain a pointer because detachFromParent will set the page to null.
+ WebCore::Page* page = pFrame->page();
+ if (fl)
+ fl->detachFromParent();
+ delete page;
+ view->deref();
+
+ SET_NATIVE_FRAME(env, obj, 0);
+#if ENABLE(WDS)
+ WDS::server()->removeFrame(pFrame);
+#endif
+}
+
+static void LoadUrl(JNIEnv *env, jobject obj, jstring url, jobject headers)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ LOG_ASSERT(pFrame, "nativeLoadUrl must take a valid frame pointer!");
+
+ WTF::String webcoreUrl = jstringToWtfString(env, url);
+ WebCore::KURL kurl(WebCore::KURL(), webcoreUrl);
+ WebCore::ResourceRequest request(kurl);
+ if (headers) {
+ // dalvikvm will raise exception if any of these fail
+ jclass mapClass = env->FindClass("java/util/Map");
+ jmethodID entrySet = env->GetMethodID(mapClass, "entrySet",
+ "()Ljava/util/Set;");
+ jobject set = env->CallObjectMethod(headers, entrySet);
+
+ jclass setClass = env->FindClass("java/util/Set");
+ jmethodID iterator = env->GetMethodID(setClass, "iterator",
+ "()Ljava/util/Iterator;");
+ jobject iter = env->CallObjectMethod(set, iterator);
+
+ jclass iteratorClass = env->FindClass("java/util/Iterator");
+ jmethodID hasNext = env->GetMethodID(iteratorClass, "hasNext", "()Z");
+ jmethodID next = env->GetMethodID(iteratorClass, "next",
+ "()Ljava/lang/Object;");
+ jclass entryClass = env->FindClass("java/util/Map$Entry");
+ jmethodID getKey = env->GetMethodID(entryClass, "getKey",
+ "()Ljava/lang/Object;");
+ jmethodID getValue = env->GetMethodID(entryClass, "getValue",
+ "()Ljava/lang/Object;");
+
+ while (env->CallBooleanMethod(iter, hasNext)) {
+ jobject entry = env->CallObjectMethod(iter, next);
+ jstring key = (jstring) env->CallObjectMethod(entry, getKey);
+ jstring value = (jstring) env->CallObjectMethod(entry, getValue);
+ request.setHTTPHeaderField(jstringToWtfString(env, key), jstringToWtfString(env, value));
+ env->DeleteLocalRef(entry);
+ env->DeleteLocalRef(key);
+ env->DeleteLocalRef(value);
+ }
+
+ env->DeleteLocalRef(entryClass);
+ env->DeleteLocalRef(iteratorClass);
+ env->DeleteLocalRef(iter);
+ env->DeleteLocalRef(setClass);
+ env->DeleteLocalRef(set);
+ env->DeleteLocalRef(mapClass);
+ }
+ LOGV("LoadUrl %s", kurl.string().latin1().data());
+ pFrame->loader()->load(request, false);
+}
+
+static void PostUrl(JNIEnv *env, jobject obj, jstring url, jbyteArray postData)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ LOG_ASSERT(pFrame, "nativePostUrl must take a valid frame pointer!");
+
+ WebCore::KURL kurl(WebCore::KURL(), jstringToWtfString(env, url));
+ WebCore::ResourceRequest request(kurl);
+ request.setHTTPMethod("POST");
+ request.setHTTPContentType("application/x-www-form-urlencoded");
+
+ if (postData) {
+ jsize size = env->GetArrayLength(postData);
+ jbyte* bytes = env->GetByteArrayElements(postData, NULL);
+ RefPtr<FormData> formData = FormData::create((const void*)bytes, size);
+ // the identifier uses the same logic as generateFormDataIdentifier() in
+ // HTMLFormElement.cpp
+ formData->setIdentifier(static_cast<int64_t>(WTF::currentTime() * 1000000.0));
+ request.setHTTPBody(formData);
+ env->ReleaseByteArrayElements(postData, bytes, 0);
+ }
+
+ LOGV("PostUrl %s", kurl.string().latin1().data());
+ WebCore::FrameLoadRequest frameRequest(pFrame->document()->securityOrigin(), request);
+ pFrame->loader()->loadFrameRequest(frameRequest, false, false, 0, 0, WebCore::SendReferrer);
+}
+
+static void LoadData(JNIEnv *env, jobject obj, jstring baseUrl, jstring data,
+ jstring mimeType, jstring encoding, jstring failUrl)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ LOG_ASSERT(pFrame, "nativeLoadData must take a valid frame pointer!");
+
+ // Setup the resource request
+ WebCore::ResourceRequest request(jstringToWtfString(env, baseUrl));
+
+ // Setup the substituteData
+ const char* dataStr = env->GetStringUTFChars(data, NULL);
+ WTF::PassRefPtr<WebCore::SharedBuffer> sharedBuffer =
+ WebCore::SharedBuffer::create();
+ LOG_ASSERT(dataStr, "nativeLoadData has a null data string.");
+ sharedBuffer->append(dataStr, strlen(dataStr));
+ env->ReleaseStringUTFChars(data, dataStr);
+
+ WebCore::SubstituteData substituteData(sharedBuffer,
+ jstringToWtfString(env, mimeType), jstringToWtfString(env, encoding),
+ WebCore::KURL(ParsedURLString, jstringToWtfString(env, failUrl)));
+
+ // Perform the load
+ pFrame->loader()->load(request, substituteData, false);
+}
+
+static void StopLoading(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ LOG_ASSERT(pFrame, "nativeStopLoading must take a valid frame pointer!");
+ LOGV("::WebCore:: stopLoading %p", pFrame);
+
+ // Stop loading the page and do not send an unload event
+ pFrame->loader()->stopForUserCancel();
+}
+
+#if ENABLE(ARCHIVE)
+static String saveArchiveAutoname(String basename, String name, String extension) {
+ if (name.isNull() || name.isEmpty()) {
+ name = String("index");
+ }
+
+ String testname = basename;
+ testname.append(name);
+ testname.append(extension);
+
+ errno = 0;
+ struct stat permissions;
+ if (stat(testname.utf8().data(), &permissions) < 0) {
+ if (errno == ENOENT)
+ return testname;
+ return String();
+ }
+
+ const int maxAttempts = 100;
+ for (int i = 1; i < maxAttempts; i++) {
+ String testname = basename;
+ testname.append(name);
+ testname.append("-");
+ testname.append(String::number(i));
+ testname.append(extension);
+
+ errno = 0;
+ if (stat(testname.utf8().data(), &permissions) < 0) {
+ if (errno == ENOENT)
+ return testname;
+ return String();
+ }
+ }
+
+ return String();
+}
+#endif
+
+static jstring SaveWebArchive(JNIEnv *env, jobject obj, jstring basename, jboolean autoname)
+{
+#if ENABLE(ARCHIVE)
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ LOG_ASSERT(pFrame, "nativeSaveWebArchive must take a valid frame pointer!");
+ String mimeType = pFrame->loader()->documentLoader()->mainResource()->mimeType();
+ if ((mimeType != "text/html") && (mimeType != "application/xhtml+xml"))
+ return NULL;
+
+ const char* basenameNative = getCharactersFromJStringInEnv(env, basename);
+ String basenameString = String::fromUTF8(basenameNative);
+ String filename;
+
+ if (autoname) {
+ String name = pFrame->loader()->documentLoader()->originalURL().lastPathComponent();
+ String extension = String(".webarchivexml");
+ filename = saveArchiveAutoname(basenameString, name, extension);
+ } else {
+ filename = basenameString;
+ }
+
+ if (filename.isNull() || filename.isEmpty()) {
+ LOGD("saveWebArchive: Failed to select a filename to save.");
+ releaseCharactersForJStringInEnv(env, basename, basenameNative);
+ return NULL;
+ }
+
+ const int noCompression = 0;
+ xmlTextWriterPtr writer = xmlNewTextWriterFilename(filename.utf8().data(), noCompression);
+ if (writer == NULL) {
+ LOGD("saveWebArchive: Failed to initialize xml writer.");
+ releaseCharactersForJStringInEnv(env, basename, basenameNative);
+ return NULL;
+ }
+
+ RefPtr<WebArchiveAndroid> archive = WebCore::WebArchiveAndroid::create(pFrame);
+
+ bool result = archive->saveWebArchive(writer);
+
+ releaseCharactersForJStringInEnv(env, basename, basenameNative);
+ xmlFreeTextWriter(writer);
+
+ if (result)
+ return wtfStringToJstring(env, filename);
+
+ return NULL;
+#endif
+}
+
+static jstring ExternalRepresentation(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ LOG_ASSERT(pFrame, "android_webcore_nativeExternalRepresentation must take a valid frame pointer!");
+
+ // Request external representation of the render tree
+ WTF::String renderDump = WebCore::externalRepresentation(pFrame);
+ return wtfStringToJstring(env, renderDump);
+}
+
+static StringBuilder FrameAsText(WebCore::Frame *pFrame, jboolean dumpChildFrames) {
+ StringBuilder renderDump;
+ if (!pFrame)
+ return renderDump;
+ WebCore::Element *documentElement = pFrame->document()->documentElement();
+ if (!documentElement)
+ return renderDump;
+ if (pFrame->tree()->parent()) {
+ renderDump.append("\n--------\nFrame: '");
+ renderDump.append(pFrame->tree()->name());
+ renderDump.append("'\n--------\n");
+ }
+ renderDump.append(((WebCore::HTMLElement*)documentElement)->innerText());
+ renderDump.append("\n");
+ if (dumpChildFrames) {
+ for (unsigned i = 0; i < pFrame->tree()->childCount(); ++i) {
+ renderDump.append(FrameAsText(pFrame->tree()->child(i), dumpChildFrames).toString());
+ }
+ }
+ return renderDump;
+}
+
+static jstring DocumentAsText(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ LOG_ASSERT(pFrame, "android_webcore_nativeDocumentAsText must take a valid frame pointer!");
+
+ WTF::String renderDump = FrameAsText(pFrame, false /* dumpChildFrames */).toString();
+ return wtfStringToJstring(env, renderDump);
+}
+
+static jstring ChildFramesAsText(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ LOG_ASSERT(pFrame, "android_webcore_nativeDocumentAsText must take a valid frame pointer!");
+
+ StringBuilder renderDumpBuilder;
+ for (unsigned i = 0; i < pFrame->tree()->childCount(); ++i) {
+ renderDumpBuilder.append(FrameAsText(pFrame->tree()->child(i), true /* dumpChildFrames */).toString());
+ }
+ WTF::String renderDump = renderDumpBuilder.toString();
+ return wtfStringToJstring(env, renderDump);
+}
+
+static void Reload(JNIEnv *env, jobject obj, jboolean allowStale)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ LOG_ASSERT(pFrame, "nativeReload must take a valid frame pointer!");
+
+ WebCore::FrameLoader* loader = pFrame->loader();
+ if (allowStale) {
+ // load the current page with FrameLoadTypeIndexedBackForward so that it
+ // will use cache when it is possible
+ WebCore::Page* page = pFrame->page();
+ WebCore::HistoryItem* item = page->backForwardList()->currentItem();
+ if (item)
+ page->goToItem(item, FrameLoadTypeIndexedBackForward);
+ } else
+ loader->reload(true);
+}
+
+static void GoBackOrForward(JNIEnv *env, jobject obj, jint pos)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ LOG_ASSERT(pFrame, "nativeGoBackOrForward must take a valid frame pointer!");
+
+ if (pos == 1)
+ pFrame->page()->goForward();
+ else if (pos == -1)
+ pFrame->page()->goBack();
+ else
+ pFrame->page()->goBackOrForward(pos);
+}
+
+static jobject StringByEvaluatingJavaScriptFromString(JNIEnv *env, jobject obj, jstring script)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ LOG_ASSERT(pFrame, "stringByEvaluatingJavaScriptFromString must take a valid frame pointer!");
+
+ WebCore::ScriptValue value =
+ pFrame->script()->executeScript(jstringToWtfString(env, script), true);
+ WTF::String result = WTF::String();
+ ScriptState* scriptState = mainWorldScriptState(pFrame);
+ if (!value.getString(scriptState, result))
+ return NULL;
+ return wtfStringToJstring(env, result);
+}
+
+// Wrap the JavaInstance used when binding custom javascript interfaces. Use a
+// weak reference so that the gc can collect the WebView. Override virtualBegin
+// and virtualEnd and swap the weak reference for the real object.
+class WeakJavaInstance : public JavaInstance {
+public:
+#if USE(JSC)
+ static PassRefPtr<WeakJavaInstance> create(jobject obj, PassRefPtr<RootObject> root)
+ {
+ return adoptRef(new WeakJavaInstance(obj, root));
+ }
+#elif USE(V8)
+ static PassRefPtr<WeakJavaInstance> create(jobject obj)
+ {
+ return adoptRef(new WeakJavaInstance(obj));
+ }
+#endif
+
+private:
+#if USE(JSC)
+ WeakJavaInstance(jobject instance, PassRefPtr<RootObject> rootObject)
+ : JavaInstance(instance, rootObject)
+#elif USE(V8)
+ WeakJavaInstance(jobject instance)
+ : JavaInstance(instance)
+#endif
+ , m_beginEndDepth(0)
+ {
+ JNIEnv* env = getJNIEnv();
+ // JavaInstance creates a global ref to instance in its constructor.
+ env->DeleteGlobalRef(m_instance->instance());
+ // Set the object to a weak reference.
+ m_instance->setInstance(env->NewWeakGlobalRef(instance));
+ }
+ ~WeakJavaInstance()
+ {
+ JNIEnv* env = getJNIEnv();
+ // Store the weak reference so we can delete it later.
+ jweak weak = m_instance->instance();
+ // The JavaInstance destructor attempts to delete the global ref stored
+ // in m_instance. Since we replaced it in our constructor with a weak
+ // reference, restore the global ref here so the vm will not complain.
+ m_instance->setInstance(env->NewGlobalRef(
+ getRealObject(env, m_instance->instance()).get()));
+ // Delete the weak reference.
+ env->DeleteWeakGlobalRef(weak);
+ }
+
+ virtual void virtualBegin()
+ {
+ if (m_beginEndDepth++ > 0)
+ return;
+ m_weakRef = m_instance->instance();
+ JNIEnv* env = getJNIEnv();
+ // This is odd. getRealObject returns an AutoJObject which is used to
+ // cleanly create and delete a local reference. But, here we need to
+ // maintain the local reference across calls to virtualBegin() and
+ // virtualEnd(). So, release the local reference from the AutoJObject
+ // and delete the local reference in virtualEnd().
+ m_realObject = getRealObject(env, m_weakRef).release();
+ // Point to the real object
+ m_instance->setInstance(m_realObject);
+ // Call the base class method
+ INHERITED::virtualBegin();
+ }
+
+ virtual void virtualEnd()
+ {
+ if (--m_beginEndDepth > 0)
+ return;
+ // Call the base class method first to pop the local frame.
+ INHERITED::virtualEnd();
+ // Get rid of the local reference to the real object.
+ getJNIEnv()->DeleteLocalRef(m_realObject);
+ // Point back to the WeakReference.
+ m_instance->setInstance(m_weakRef);
+ }
+
+private:
+ typedef JavaInstance INHERITED;
+ jobject m_realObject;
+ jweak m_weakRef;
+ // The current depth of nested calls to virtualBegin and virtualEnd.
+ int m_beginEndDepth;
+};
+
+static void AddJavascriptInterface(JNIEnv *env, jobject obj, jint nativeFramePointer,
+ jobject javascriptObj, jstring interfaceName)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ WebCore::Frame* pFrame = 0;
+ if (nativeFramePointer == 0)
+ pFrame = GET_NATIVE_FRAME(env, obj);
+ else
+ pFrame = (WebCore::Frame*)nativeFramePointer;
+ LOG_ASSERT(pFrame, "nativeAddJavascriptInterface must take a valid frame pointer!");
+
+ JavaVM* vm;
+ env->GetJavaVM(&vm);
+ LOGV("::WebCore:: addJSInterface: %p", pFrame);
+
+#if USE(JSC)
+ // Copied from qwebframe.cpp
+ JSC::JSLock lock(JSC::SilenceAssertionsOnly);
+ WebCore::JSDOMWindow *window = WebCore::toJSDOMWindow(pFrame, mainThreadNormalWorld());
+ if (window) {
+ RootObject *root = pFrame->script()->bindingRootObject();
+ setJavaVM(vm);
+ // Add the binding to JS environment
+ JSC::ExecState* exec = window->globalExec();
+ JSC::JSObject* addedObject = WeakJavaInstance::create(javascriptObj,
+ root)->createRuntimeObject(exec);
+ const jchar* s = env->GetStringChars(interfaceName, NULL);
+ if (s) {
+ // Add the binding name to the window's table of child objects.
+ JSC::PutPropertySlot slot;
+ window->put(exec, JSC::Identifier(exec, (const UChar *)s,
+ env->GetStringLength(interfaceName)), addedObject, slot);
+ env->ReleaseStringChars(interfaceName, s);
+ checkException(env);
+ }
+ }
+#elif USE(V8)
+ if (pFrame) {
+ RefPtr<JavaInstance> addedObject = WeakJavaInstance::create(javascriptObj);
+ const char* name = getCharactersFromJStringInEnv(env, interfaceName);
+ // Pass ownership of the added object to bindToWindowObject.
+ NPObject* npObject = JavaInstanceToNPObject(addedObject.get());
+ pFrame->script()->bindToWindowObject(pFrame, name, npObject);
+ // bindToWindowObject calls NPN_RetainObject on the
+ // returned one (see createV8ObjectForNPObject in V8NPObject.cpp).
+ // bindToWindowObject also increases obj's ref count and decreases
+ // the ref count when the object is not reachable from JavaScript
+ // side. Code here must release the reference count increased by
+ // bindToWindowObject.
+
+ // Note that while this function is declared in WebCore/bridge/npruntime.h, for V8 builds
+ // we use WebCore/bindings/v8/npruntime.cpp (rather than
+ // WebCore/bridge/npruntime.cpp), so the function is implemented there.
+ // TODO: Combine the two versions of these NPAPI files.
+ NPN_ReleaseObject(npObject);
+ releaseCharactersForJString(interfaceName, name);
+ }
+#endif
+
+}
+
+static void SetCacheDisabled(JNIEnv *env, jobject obj, jboolean disabled)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ WebCore::cache()->setDisabled(disabled);
+}
+
+static jboolean CacheDisabled(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ return WebCore::cache()->disabled();
+}
+
+static void ClearWebCoreCache()
+{
+ if (!WebCore::cache()->disabled()) {
+ // Disabling the cache will remove all resources from the cache. They may
+ // still live on if they are referenced by some Web page though.
+ WebCore::cache()->setDisabled(true);
+ WebCore::cache()->setDisabled(false);
+ }
+
+ // clear page cache
+ int pageCapacity = WebCore::pageCache()->capacity();
+ // Setting size to 0, makes all pages be released.
+ WebCore::pageCache()->setCapacity(0);
+ WebCore::pageCache()->releaseAutoreleasedPagesNow();
+ WebCore::pageCache()->setCapacity(pageCapacity);
+}
+
+static void ClearWebViewCache()
+{
+#if USE(CHROME_NETWORK_STACK)
+ WebCache::get(false /*privateBrowsing*/)->clear();
+#else
+ // The Android network stack provides a WebView cache in CacheManager.java.
+ // Clearing this is handled entirely Java-side.
+#endif
+}
+
+static void ClearCache(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#if USE(JSC)
+ JSC::JSLock lock(false);
+ JSC::Heap::Statistics jsHeapStatistics = WebCore::JSDOMWindow::commonJSGlobalData()->heap.statistics();
+ LOGD("About to gc and JavaScript heap size is %d and has %d bytes free",
+ jsHeapStatistics.size, jsHeapStatistics.free);
+#endif // USE(JSC)
+ LOGD("About to clear cache and current cache has %d bytes live and %d bytes dead",
+ cache()->getLiveSize(), cache()->getDeadSize());
+#endif // ANDROID_INSTRUMENT
+ ClearWebCoreCache();
+ ClearWebViewCache();
+#if USE(JSC)
+ // force JavaScript to GC when clear cache
+ WebCore::gcController().garbageCollectSoon();
+#elif USE(V8)
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ pFrame->script()->lowMemoryNotification();
+#endif // USE(JSC)
+}
+
+static jboolean DocumentHasImages(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ LOG_ASSERT(pFrame, "DocumentHasImages must take a valid frame pointer!");
+
+ return pFrame->document()->images()->length() > 0;
+}
+
+static jboolean HasPasswordField(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ LOG_ASSERT(pFrame, "HasPasswordField must take a valid frame pointer!");
+
+ bool found = false;
+ WTF::PassRefPtr<WebCore::HTMLCollection> form = pFrame->document()->forms();
+ WebCore::Node* node = form->firstItem();
+ // Null/Empty namespace means that node is not created in HTMLFormElement
+ // class, but just normal Element class.
+ while (node && !found && !node->namespaceURI().isNull() &&
+ !node->namespaceURI().isEmpty()) {
+ const WTF::Vector<WebCore::FormAssociatedElement*>& elements =
+ ((WebCore::HTMLFormElement*)node)->associatedElements();
+ size_t size = elements.size();
+ for (size_t i = 0; i< size && !found; i++) {
+ WebCore::HTMLElement* e = toHTMLElement(elements[i]);
+ if (e->hasLocalName(WebCore::HTMLNames::inputTag)) {
+ if (static_cast<WebCore::HTMLInputElement*>(e)->isPasswordField())
+ found = true;
+ }
+ }
+ node = form->nextItem();
+ }
+ return found;
+}
+
+static jobjectArray GetUsernamePassword(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ LOG_ASSERT(pFrame, "GetUsernamePassword must take a valid frame pointer!");
+ jobjectArray strArray = NULL;
+ WTF::String username;
+ WTF::String password;
+ if (WebFrame::getWebFrame(pFrame)->getUsernamePasswordFromDom(pFrame, username, password)) {
+ jclass stringClass = env->FindClass("java/lang/String");
+ strArray = env->NewObjectArray(2, stringClass, NULL);
+ env->DeleteLocalRef(stringClass);
+ env->SetObjectArrayElement(strArray, 0, wtfStringToJstring(env, username));
+ env->SetObjectArrayElement(strArray, 1, wtfStringToJstring(env, password));
+ }
+ return strArray;
+}
+
+static void SetUsernamePassword(JNIEnv *env, jobject obj,
+ jstring username, jstring password)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ LOG_ASSERT(pFrame, "SetUsernamePassword must take a valid frame pointer!");
+
+ WebCore::HTMLInputElement* usernameEle = NULL;
+ WebCore::HTMLInputElement* passwordEle = NULL;
+ bool found = false;
+ WTF::PassRefPtr<WebCore::HTMLCollection> form = pFrame->document()->forms();
+ WebCore::Node* node = form->firstItem();
+ while (node && !found && !node->namespaceURI().isNull() &&
+ !node->namespaceURI().isEmpty()) {
+ const WTF::Vector<WebCore::FormAssociatedElement*>& elements =
+ ((WebCore::HTMLFormElement*)node)->associatedElements();
+ size_t size = elements.size();
+ for (size_t i = 0; i< size && !found; i++) {
+ WebCore::HTMLElement* e = toHTMLElement(elements[i]);
+ if (e->hasLocalName(WebCore::HTMLNames::inputTag)) {
+ WebCore::HTMLInputElement* input = (WebCore::HTMLInputElement*)e;
+ if (input->autoComplete() == false)
+ continue;
+ if (input->isPasswordField())
+ passwordEle = input;
+ else if (input->isTextField() || input->isEmailField())
+ usernameEle = input;
+ if (usernameEle != NULL && passwordEle != NULL)
+ found = true;
+ }
+ }
+ node = form->nextItem();
+ }
+ if (found) {
+ usernameEle->setValue(jstringToWtfString(env, username));
+ passwordEle->setValue(jstringToWtfString(env, password));
+ }
+}
+
+void
+WebFrame::saveFormData(HTMLFormElement* form)
+{
+ if (form->autoComplete()) {
+ JNIEnv* env = getJNIEnv();
+ jclass mapClass = env->FindClass("java/util/HashMap");
+ LOG_ASSERT(mapClass, "Could not find HashMap class!");
+ jmethodID init = env->GetMethodID(mapClass, "<init>", "(I)V");
+ LOG_ASSERT(init, "Could not find constructor for HashMap");
+ jobject hashMap = env->NewObject(mapClass, init, 1);
+ LOG_ASSERT(hashMap, "Could not create a new HashMap");
+ jmethodID put = env->GetMethodID(mapClass, "put",
+ "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+ LOG_ASSERT(put, "Could not find put method on HashMap");
+ WTF::Vector<WebCore::FormAssociatedElement*> elements = form->associatedElements();
+ size_t size = elements.size();
+ for (size_t i = 0; i < size; i++) {
+ WebCore::HTMLElement* e = toHTMLElement(elements[i]);
+ if (e->hasTagName(WebCore::HTMLNames::inputTag)) {
+ WebCore::HTMLInputElement* input = static_cast<WebCore::HTMLInputElement*>(e);
+ if (input->isTextField() && !input->isPasswordField()
+ && input->autoComplete()) {
+ WTF::String value = input->value();
+ int len = value.length();
+ if (len) {
+ const WTF::AtomicString& name = input->name();
+ jstring key = wtfStringToJstring(env, name);
+ jstring val = wtfStringToJstring(env, value);
+ LOG_ASSERT(key && val, "name or value not set");
+ env->CallObjectMethod(hashMap, put, key, val);
+ env->DeleteLocalRef(key);
+ env->DeleteLocalRef(val);
+ }
+ }
+ }
+ }
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mSaveFormData, hashMap);
+ env->DeleteLocalRef(hashMap);
+ env->DeleteLocalRef(mapClass);
+ }
+}
+
+static void OrientationChanged(JNIEnv *env, jobject obj, int orientation)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::NativeCallbackTimeCounter);
+#endif
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ LOGV("Sending orientation: %d", orientation);
+ pFrame->sendOrientationChangeEvent(orientation);
+}
+
+#if USE(CHROME_NETWORK_STACK)
+
+static void AuthenticationProceed(JNIEnv *env, jobject obj, int handle, jstring jUsername, jstring jPassword)
+{
+ WebUrlLoaderClient* client = reinterpret_cast<WebUrlLoaderClient*>(handle);
+ std::string username = jstringToStdString(env, jUsername);
+ std::string password = jstringToStdString(env, jPassword);
+ client->setAuth(username, password);
+}
+
+static void AuthenticationCancel(JNIEnv *env, jobject obj, int handle)
+{
+ WebUrlLoaderClient* client = reinterpret_cast<WebUrlLoaderClient*>(handle);
+ client->cancelAuth();
+}
+
+static void SslCertErrorProceed(JNIEnv *env, jobject obj, int handle)
+{
+ WebUrlLoaderClient* client = reinterpret_cast<WebUrlLoaderClient*>(handle);
+ client->proceedSslCertError();
+}
+
+static void SslCertErrorCancel(JNIEnv *env, jobject obj, int handle, int cert_error)
+{
+ WebUrlLoaderClient* client = reinterpret_cast<WebUrlLoaderClient*>(handle);
+ client->cancelSslCertError(cert_error);
+}
+
+#else
+
+static void AuthenticationProceed(JNIEnv *env, jobject obj, int handle, jstring jUsername, jstring jPassword)
+{
+ LOGW("Chromium authentication API called, but libchromium is not available");
+}
+
+static void AuthenticationCancel(JNIEnv *env, jobject obj, int handle)
+{
+ LOGW("Chromium authentication API called, but libchromium is not available");
+}
+
+static void SslCertErrorProceed(JNIEnv *env, jobject obj, int handle)
+{
+ LOGW("Chromium SSL API called, but libchromium is not available");
+}
+
+static void SslCertErrorCancel(JNIEnv *env, jobject obj, int handle, int cert_error)
+{
+ LOGW("Chromium SSL API called, but libchromium is not available");
+}
+
+#endif // USE(CHROME_NETWORK_STACK)
+
+// ----------------------------------------------------------------------------
+
+/*
+ * JNI registration.
+ */
+static JNINativeMethod gBrowserFrameNativeMethods[] = {
+ /* name, signature, funcPtr */
+ { "nativeCallPolicyFunction", "(II)V",
+ (void*) CallPolicyFunction },
+ { "nativeCreateFrame", "(Landroid/webkit/WebViewCore;Landroid/content/res/AssetManager;Landroid/webkit/WebBackForwardList;)V",
+ (void*) CreateFrame },
+ { "nativeDestroyFrame", "()V",
+ (void*) DestroyFrame },
+ { "nativeStopLoading", "()V",
+ (void*) StopLoading },
+ { "nativeLoadUrl", "(Ljava/lang/String;Ljava/util/Map;)V",
+ (void*) LoadUrl },
+ { "nativePostUrl", "(Ljava/lang/String;[B)V",
+ (void*) PostUrl },
+ { "nativeLoadData", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
+ (void*) LoadData },
+ { "nativeSaveWebArchive", "(Ljava/lang/String;Z)Ljava/lang/String;",
+ (void*) SaveWebArchive },
+ { "externalRepresentation", "()Ljava/lang/String;",
+ (void*) ExternalRepresentation },
+ { "documentAsText", "()Ljava/lang/String;",
+ (void*) DocumentAsText },
+ { "childFramesAsText", "()Ljava/lang/String;",
+ (void*) ChildFramesAsText },
+ { "reload", "(Z)V",
+ (void*) Reload },
+ { "nativeGoBackOrForward", "(I)V",
+ (void*) GoBackOrForward },
+ { "nativeAddJavascriptInterface", "(ILjava/lang/Object;Ljava/lang/String;)V",
+ (void*) AddJavascriptInterface },
+ { "stringByEvaluatingJavaScriptFromString",
+ "(Ljava/lang/String;)Ljava/lang/String;",
+ (void*) StringByEvaluatingJavaScriptFromString },
+ { "setCacheDisabled", "(Z)V",
+ (void*) SetCacheDisabled },
+ { "cacheDisabled", "()Z",
+ (void*) CacheDisabled },
+ { "clearCache", "()V",
+ (void*) ClearCache },
+ { "documentHasImages", "()Z",
+ (void*) DocumentHasImages },
+ { "hasPasswordField", "()Z",
+ (void*) HasPasswordField },
+ { "getUsernamePassword", "()[Ljava/lang/String;",
+ (void*) GetUsernamePassword },
+ { "setUsernamePassword", "(Ljava/lang/String;Ljava/lang/String;)V",
+ (void*) SetUsernamePassword },
+ { "nativeOrientationChanged", "(I)V",
+ (void*) OrientationChanged },
+ { "nativeAuthenticationProceed", "(ILjava/lang/String;Ljava/lang/String;)V",
+ (void*) AuthenticationProceed },
+ { "nativeAuthenticationCancel", "(I)V",
+ (void*) AuthenticationCancel },
+ { "nativeSslCertErrorProceed", "(I)V",
+ (void*) SslCertErrorProceed },
+ { "nativeSslCertErrorCancel", "(II)V",
+ (void*) SslCertErrorCancel },
+};
+
+int registerWebFrame(JNIEnv* env)
+{
+ jclass clazz = env->FindClass("android/webkit/BrowserFrame");
+ LOG_ASSERT(clazz, "Cannot find BrowserFrame");
+ gFrameField = env->GetFieldID(clazz, "mNativeFrame", "I");
+ LOG_ASSERT(gFrameField, "Cannot find mNativeFrame on BrowserFrame");
+ env->DeleteLocalRef(clazz);
+
+ return jniRegisterNativeMethods(env, "android/webkit/BrowserFrame",
+ gBrowserFrameNativeMethods, NELEM(gBrowserFrameNativeMethods));
+}
+
+} /* namespace android */
diff --git a/Source/WebKit/android/jni/WebCoreFrameBridge.h b/Source/WebKit/android/jni/WebCoreFrameBridge.h
new file mode 100644
index 0000000..6522a5f
--- /dev/null
+++ b/Source/WebKit/android/jni/WebCoreFrameBridge.h
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+
+// TODO: change name to WebFrame.h
+
+#ifndef WEBFRAME_H
+#define WEBFRAME_H
+
+#include "FrameLoaderClient.h"
+#include "PlatformBridge.h"
+#include "PlatformString.h"
+#include "WebCoreRefObject.h"
+#include <jni.h>
+#include <string>
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+ class HTMLFormElement;
+ class Frame;
+ class HistoryItem;
+ class Image;
+ class Page;
+ class RenderPart;
+ class RenderSkinAndroid;
+ class ResourceHandle;
+ class ResourceLoaderAndroid;
+ class ResourceRequest;
+}
+
+namespace android {
+
+class WebViewCore;
+class WebUrlLoaderClient;
+class UrlInterceptResponse;
+
+// one instance of WebFrame per Page for calling into Java's BrowserFrame
+class WebFrame : public WebCoreRefObject {
+ public:
+ WebFrame(JNIEnv* env, jobject obj, jobject historyList, WebCore::Page* page);
+ ~WebFrame();
+
+ // helper function
+ static WebFrame* getWebFrame(const WebCore::Frame* frame);
+
+ virtual PassRefPtr<WebCore::ResourceLoaderAndroid> startLoadingResource(WebCore::ResourceHandle*,
+ const WebCore::ResourceRequest& request, bool mainResource,
+ bool synchronous);
+
+ UrlInterceptResponse* shouldInterceptRequest(const WTF::String& url);
+
+ void reportError(int errorCode, const WTF::String& description,
+ const WTF::String& failingUrl);
+
+ void loadStarted(WebCore::Frame* frame);
+
+ void transitionToCommitted(WebCore::Frame* frame);
+
+ void didFinishLoad(WebCore::Frame* frame);
+
+ void addHistoryItem(WebCore::HistoryItem* item);
+
+ void removeHistoryItem(int index);
+
+ void updateHistoryIndex(int newIndex);
+
+ void setTitle(const WTF::String& title);
+
+ void windowObjectCleared(WebCore::Frame* frame);
+
+ void setProgress(float newProgress);
+
+ const WTF::String userAgentForURL(const WebCore::KURL* url);
+
+ void didReceiveIcon(WebCore::Image* icon);
+
+ void didReceiveTouchIconURL(const WTF::String& url, bool precomposed);
+
+ void updateVisitedHistory(const WebCore::KURL& url, bool reload);
+
+ virtual bool canHandleRequest(const WebCore::ResourceRequest& request);
+
+ WebCore::Frame* createWindow(bool dialog, bool userGesture);
+
+ void requestFocus() const;
+
+ void closeWindow(WebViewCore* webViewCore);
+
+ void decidePolicyForFormResubmission(WebCore::FramePolicyFunction func);
+
+ void setUserAgent(WTF::String userAgent) { mUserAgent = userAgent; }
+
+ WTF::String getRawResourceFilename(WebCore::PlatformBridge::rawResId) const;
+
+ float density() const;
+
+ void didReceiveAuthenticationChallenge(WebUrlLoaderClient*, const std::string& host, const std::string& realm, bool useCachedCredentials);
+
+ void reportSslCertError(WebUrlLoaderClient* client, int cert_error, const std::string& cert);
+
+ void downloadStart(const std::string& url, const std::string& userAgent, const std::string& contentDisposition, const std::string& mimetype, long long contentLength);
+
+ void didReceiveData(const char* data, int size);
+
+ void didFinishLoading();
+
+ void maybeSavePassword(WebCore::Frame* frame, const WebCore::ResourceRequest& request);
+
+ void setCertificate(const std::string& cert);
+
+ // Parse the x-auto-login header and propagate the parameters to the
+ // application.
+ void autoLogin(const std::string& loginHeader);
+
+ /**
+ * When the user initiates a click, we set mUserInitiatedAction to true.
+ * If a load happens due to this click, then we ask the application if it wants
+ * to override the load. Otherwise, we attempt to load the resource internally.
+ */
+ void setUserInitiatedAction(bool userInitiatedAction) { mUserInitiatedAction = userInitiatedAction; }
+
+ WebCore::Page* page() const { return mPage; }
+
+ // Currently used only by the chrome net stack. A similar field is used by
+ // FrameLoader.java to block java network loads.
+ void setBlockNetworkLoads(bool block) { mBlockNetworkLoads = block; }
+ bool blockNetworkLoads() const { return mBlockNetworkLoads; }
+
+ /**
+ * Helper methods. These are typically chunks of code that are called in
+ * slightly different ways by the Apache and Chrome HTTP stacks.
+ */
+ bool getUsernamePasswordFromDom(WebCore::Frame* frame, WTF::String& username, WTF::String& password);
+ jbyteArray getPostData(const WebCore::ResourceRequest& request);
+
+ bool shouldSaveFormData();
+ void saveFormData(WebCore::HTMLFormElement*);
+ const WebCore::RenderSkinAndroid* renderSkins() const { return m_renderSkins; }
+ void setRenderSkins(const WebCore::RenderSkinAndroid* skins) { m_renderSkins = skins; }
+private:
+ struct JavaBrowserFrame;
+ JavaBrowserFrame* mJavaFrame;
+ WebCore::Page* mPage;
+ WTF::String mUserAgent;
+ bool mBlockNetworkLoads;
+ bool mUserInitiatedAction;
+ const WebCore::RenderSkinAndroid* m_renderSkins;
+};
+
+} // namespace android
+
+#endif // WEBFRAME_H
diff --git a/Source/WebKit/android/jni/WebCoreJni.cpp b/Source/WebKit/android/jni/WebCoreJni.cpp
new file mode 100644
index 0000000..2a07999
--- /dev/null
+++ b/Source/WebKit/android/jni/WebCoreJni.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2007, 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 "WebCoreJni.h"
+
+#include "NotImplemented.h"
+#include <JNIUtility.h>
+#include <jni.h>
+#include <utils/Log.h>
+
+namespace android {
+
+AutoJObject getRealObject(JNIEnv* env, jobject obj)
+{
+ jobject real = env->NewLocalRef(obj);
+ LOG_ASSERT(real, "The real object has been deleted!");
+ return AutoJObject(env, real);
+}
+
+/**
+ * Helper method for checking java exceptions
+ * @return true if an exception occurred.
+ */
+bool checkException(JNIEnv* env)
+{
+ if (env->ExceptionCheck() != 0)
+ {
+ LOGE("*** Uncaught exception returned from Java call!\n");
+ env->ExceptionDescribe();
+ return true;
+ }
+ return false;
+}
+
+// This method is safe to call from the ui thread and the WebCore thread.
+WTF::String jstringToWtfString(JNIEnv* env, jstring str)
+{
+ if (!str || !env)
+ return WTF::String();
+ const jchar* s = env->GetStringChars(str, NULL);
+ if (!s)
+ return WTF::String();
+ WTF::String ret(s, env->GetStringLength(str));
+ env->ReleaseStringChars(str, s);
+ checkException(env);
+ return ret;
+}
+
+jstring wtfStringToJstring(JNIEnv* env, const WTF::String& str, bool validOnZeroLength)
+{
+ int length = str.length();
+ return length || validOnZeroLength ? env->NewString(str.characters(), length) : 0;
+}
+
+
+#if USE(CHROME_NETWORK_STACK)
+string16 jstringToString16(JNIEnv* env, jstring jstr)
+{
+ if (!jstr || !env)
+ return string16();
+
+ const char* s = env->GetStringUTFChars(jstr, 0);
+ if (!s)
+ return string16();
+ string16 str = UTF8ToUTF16(s);
+ env->ReleaseStringUTFChars(jstr, s);
+ checkException(env);
+ return str;
+}
+
+std::string jstringToStdString(JNIEnv* env, jstring jstr)
+{
+ if (!jstr || !env)
+ return std::string();
+
+ const char* s = env->GetStringUTFChars(jstr, 0);
+ if (!s)
+ return std::string();
+ std::string str(s);
+ env->ReleaseStringUTFChars(jstr, s);
+ checkException(env);
+ return str;
+}
+
+jstring stdStringToJstring(JNIEnv* env, const std::string& str, bool validOnZeroLength)
+{
+ return !str.empty() || validOnZeroLength ? env->NewStringUTF(str.c_str()) : 0;
+}
+
+#endif
+
+}
diff --git a/Source/WebKit/android/jni/WebCoreJni.h b/Source/WebKit/android/jni/WebCoreJni.h
new file mode 100644
index 0000000..ec25c8f
--- /dev/null
+++ b/Source/WebKit/android/jni/WebCoreJni.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2008, 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.
+ */
+
+#ifndef ANDROID_WEBKIT_WEBCOREJNI_H
+#define ANDROID_WEBKIT_WEBCOREJNI_H
+
+#include "ChromiumIncludes.h"
+#include "PlatformString.h"
+#include <jni.h>
+
+namespace android {
+
+// A helper class that automatically deletes the local reference to the jobject
+// returned from getRealObject.
+class AutoJObject {
+public:
+ AutoJObject(const AutoJObject& other)
+ : m_env(other.m_env)
+ , m_obj(other.m_obj ? other.m_env->NewLocalRef(other.m_obj) : NULL) {}
+ ~AutoJObject() {
+ if (m_obj)
+ m_env->DeleteLocalRef(m_obj);
+ }
+ jobject get() const {
+ return m_obj;
+ }
+ // Releases the local reference to the caller. The caller *must* delete the
+ // local reference when it is done with it.
+ jobject release() {
+ jobject obj = m_obj;
+ m_obj = 0;
+ return obj;
+ }
+ JNIEnv* env() const {
+ return m_env;
+ }
+private:
+ AutoJObject(); // Not permitted.
+ AutoJObject(JNIEnv* env, jobject obj)
+ : m_env(env)
+ , m_obj(obj) {}
+ JNIEnv* m_env;
+ jobject m_obj;
+ friend AutoJObject getRealObject(JNIEnv*, jobject);
+};
+
+// Get the real object stored in the weak reference returned as an
+// AutoJObject.
+AutoJObject getRealObject(JNIEnv*, jobject);
+
+// Helper method for check java exceptions. Returns true if an exception
+// occurred and logs the exception.
+bool checkException(JNIEnv* env);
+
+// Create a WTF::String object from a jstring object.
+WTF::String jstringToWtfString(JNIEnv*, jstring);
+// Returns a local reference to a new jstring. If validOnZeroLength is true then
+// passing in an empty WTF String will result in an empty jstring. Otherwise
+// an empty WTF String returns 0.
+jstring wtfStringToJstring(JNIEnv*, const WTF::String&, bool validOnZeroLength = false);
+
+#if USE(CHROME_NETWORK_STACK)
+string16 jstringToString16(JNIEnv*, jstring);
+
+std::string jstringToStdString(JNIEnv*, jstring);
+// Returns a local reference to a new jstring. If validOnZeroLength is true then
+// passing in an empty std::string will result in an empty jstring. Otherwise
+// an empty std::string returns 0.
+jstring stdStringToJstring(JNIEnv*, const std::string&, bool validOnZeroLength = false);
+#endif
+
+}
+
+#endif
diff --git a/Source/WebKit/android/jni/WebCoreJniOnLoad.cpp b/Source/WebKit/android/jni/WebCoreJniOnLoad.cpp
new file mode 100644
index 0000000..1f264a2
--- /dev/null
+++ b/Source/WebKit/android/jni/WebCoreJniOnLoad.cpp
@@ -0,0 +1,319 @@
+/*
+ * Copyright 2009, 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 "BackForwardList.h"
+#include "ChromeClientAndroid.h"
+#include "ContextMenuClientAndroid.h"
+#include "CookieClient.h"
+#include "DeviceMotionClientAndroid.h"
+#include "DeviceOrientationClientAndroid.h"
+#include "DragClientAndroid.h"
+#include "EditorClientAndroid.h"
+#include "FocusController.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameLoaderClientAndroid.h"
+#include "FrameView.h"
+#include "GraphicsContext.h"
+#include "HistoryItem.h"
+#include "InspectorClientAndroid.h"
+#include "IntRect.h"
+#include "JavaSharedClient.h"
+#include "Page.h"
+#include "PlatformGraphicsContext.h"
+#include "ResourceRequest.h"
+#include "ScriptController.h"
+#include "SecurityOrigin.h"
+#include "SelectionController.h"
+#include "Settings.h"
+#include "SharedBuffer.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkImageEncoder.h"
+#include "SubstituteData.h"
+#include "TimerClient.h"
+#include "TextEncoding.h"
+#include "WebCoreViewBridge.h"
+#include "WebFrameView.h"
+#include "WebViewCore.h"
+#include "benchmark/Intercept.h"
+#include "benchmark/MyJavaVM.h"
+
+#include <JNIUtility.h>
+#include <jni.h>
+#include <utils/Log.h>
+
+#define EXPORT __attribute__((visibility("default")))
+
+namespace android {
+
+extern int registerWebFrame(JNIEnv*);
+extern int registerJavaBridge(JNIEnv*);
+extern int registerJniUtil(JNIEnv*);
+extern int registerResourceLoader(JNIEnv*);
+extern int registerWebViewCore(JNIEnv*);
+extern int registerWebHistory(JNIEnv*);
+extern int registerWebIconDatabase(JNIEnv*);
+extern int registerWebSettings(JNIEnv*);
+extern int registerWebView(JNIEnv*);
+#if ENABLE(DATABASE)
+extern int registerWebStorage(JNIEnv*);
+#endif
+extern int registerGeolocationPermissions(JNIEnv*);
+extern int registerMockGeolocation(JNIEnv*);
+#if ENABLE(VIDEO)
+extern int registerMediaPlayerAudio(JNIEnv*);
+extern int registerMediaPlayerVideo(JNIEnv*);
+#endif
+extern int registerDeviceMotionAndOrientationManager(JNIEnv*);
+extern int registerCookieManager(JNIEnv*);
+#if USE(CHROME_NETWORK_STACK)
+extern int registerCacheManager(JNIEnv*);
+#endif
+
+}
+
+struct RegistrationMethod {
+ const char* name;
+ int (*func)(JNIEnv*);
+};
+
+static RegistrationMethod gWebCoreRegMethods[] = {
+ { "JavaBridge", android::registerJavaBridge },
+ { "JniUtil", android::registerJniUtil },
+ { "WebFrame", android::registerWebFrame },
+ { "WebCoreResourceLoader", android::registerResourceLoader },
+ { "WebViewCore", android::registerWebViewCore },
+ { "WebHistory", android::registerWebHistory },
+ { "WebIconDatabase", android::registerWebIconDatabase },
+ { "WebSettings", android::registerWebSettings },
+#if ENABLE(DATABASE)
+ { "WebStorage", android::registerWebStorage },
+#endif
+ { "WebView", android::registerWebView },
+ { "GeolocationPermissions", android::registerGeolocationPermissions },
+ { "MockGeolocation", android::registerMockGeolocation },
+#if ENABLE(VIDEO)
+ { "HTML5Audio", android::registerMediaPlayerAudio },
+ { "HTML5VideoViewProxy", android::registerMediaPlayerVideo },
+#endif
+ { "DeviceMotionAndOrientationManager", android::registerDeviceMotionAndOrientationManager },
+ { "CookieManager", android::registerCookieManager },
+#if USE(CHROME_NETWORK_STACK)
+ { "CacheManager", android::registerCacheManager },
+#endif
+};
+
+EXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
+{
+ // Save the JavaVM pointer for use globally.
+ JSC::Bindings::setJavaVM(vm);
+
+ JNIEnv* env = NULL;
+ jint result = -1;
+
+ if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+ LOGE("GetEnv failed!");
+ return result;
+ }
+ LOG_ASSERT(env, "Could not retrieve the env!");
+
+ const RegistrationMethod* method = gWebCoreRegMethods;
+ const RegistrationMethod* end = method + sizeof(gWebCoreRegMethods)/sizeof(RegistrationMethod);
+ while (method != end) {
+ if (method->func(env) < 0) {
+ LOGE("%s registration failed!", method->name);
+ return result;
+ }
+ method++;
+ }
+
+ // Initialize rand() function. The rand() function is used in
+ // FileSystemAndroid to create a random temporary filename.
+ srand(time(NULL));
+
+ return JNI_VERSION_1_4;
+}
+
+class MyJavaSharedClient : public TimerClient, public CookieClient {
+public:
+ MyJavaSharedClient() : m_hasTimer(false) {}
+ virtual void setSharedTimer(long long timemillis) { m_hasTimer = true; }
+ virtual void stopSharedTimer() { m_hasTimer = false; }
+ virtual void setSharedTimerCallback(void (*f)()) { m_func = f; }
+ virtual void signalServiceFuncPtrQueue() {}
+
+ // Cookie methods that do nothing.
+ virtual void setCookies(const KURL&, const String&) {}
+ virtual String cookies(const KURL&) { return ""; }
+ virtual bool cookiesEnabled() { return false; }
+
+ bool m_hasTimer;
+ void (*m_func)();
+};
+
+static void historyItemChanged(HistoryItem* i) {
+ if (i->bridge())
+ i->bridge()->updateHistoryItem(i);
+}
+
+namespace android {
+
+EXPORT void benchmark(const char* url, int reloadCount, int width, int height) {
+ ScriptController::initializeThreading();
+
+ // Setting this allows data: urls to load from a local file.
+ SecurityOrigin::setLocalLoadPolicy(SecurityOrigin::AllowLocalLoadsForAll);
+
+ // Create the fake JNIEnv and JavaVM
+ InitializeJavaVM();
+
+ // The real function is private to libwebcore but we know what it does.
+ notifyHistoryItemChanged = historyItemChanged;
+
+ // Implement the shared timer callback
+ MyJavaSharedClient client;
+ JavaSharedClient::SetTimerClient(&client);
+ JavaSharedClient::SetCookieClient(&client);
+
+ // Create the page with all the various clients
+ ChromeClientAndroid* chrome = new ChromeClientAndroid;
+ EditorClientAndroid* editor = new EditorClientAndroid;
+ DeviceMotionClientAndroid* deviceMotion = new DeviceMotionClientAndroid;
+ DeviceOrientationClientAndroid* deviceOrientation = new DeviceOrientationClientAndroid;
+ WebCore::Page::PageClients pageClients;
+ pageClients.chromeClient = chrome;
+ pageClients.contextMenuClient = new ContextMenuClientAndroid;
+ pageClients.editorClient = editor;
+ pageClients.dragClient = new DragClientAndroid;
+ pageClients.inspectorClient = new InspectorClientAndroid;
+ pageClients.deviceMotionClient = deviceMotion;
+ pageClients.deviceOrientationClient = deviceOrientation;
+ WebCore::Page* page = new WebCore::Page(pageClients);
+ editor->setPage(page);
+
+ // Create MyWebFrame that intercepts network requests
+ MyWebFrame* webFrame = new MyWebFrame(page);
+ webFrame->setUserAgent("Performance testing"); // needs to be non-empty
+ chrome->setWebFrame(webFrame);
+ // ChromeClientAndroid maintains the reference.
+ Release(webFrame);
+
+ // Create the Frame and the FrameLoaderClient
+ FrameLoaderClientAndroid* loader = new FrameLoaderClientAndroid(webFrame);
+ RefPtr<Frame> frame = Frame::create(page, NULL, loader);
+ loader->setFrame(frame.get());
+
+ // Build our View system, resize it to the given dimensions and release our
+ // references. Note: We keep a referenec to frameView so we can layout and
+ // draw later without risk of it being deleted.
+ WebViewCore* webViewCore = new WebViewCore(JSC::Bindings::getJNIEnv(),
+ MY_JOBJECT, frame.get());
+ RefPtr<FrameView> frameView = FrameView::create(frame.get());
+ WebFrameView* webFrameView = new WebFrameView(frameView.get(), webViewCore);
+ frame->setView(frameView);
+ frameView->resize(width, height);
+ Release(webViewCore);
+ Release(webFrameView);
+
+ // Initialize the frame and turn of low-bandwidth display (it fails an
+ // assertion in the Cache code)
+ frame->init();
+ frame->selection()->setFocused(true);
+ frame->page()->focusController()->setFocused(true);
+
+ deviceMotion->setWebViewCore(webViewCore);
+ deviceOrientation->setWebViewCore(webViewCore);
+
+ // Set all the default settings the Browser normally uses.
+ Settings* s = frame->settings();
+#ifdef ANDROID_LAYOUT
+ s->setLayoutAlgorithm(Settings::kLayoutNormal); // Normal layout for now
+#endif
+ s->setStandardFontFamily("sans-serif");
+ s->setFixedFontFamily("monospace");
+ s->setSansSerifFontFamily("sans-serif");
+ s->setSerifFontFamily("serif");
+ s->setCursiveFontFamily("cursive");
+ s->setFantasyFontFamily("fantasy");
+ s->setMinimumFontSize(8);
+ s->setMinimumLogicalFontSize(8);
+ s->setDefaultFontSize(16);
+ s->setDefaultFixedFontSize(13);
+ s->setLoadsImagesAutomatically(true);
+ s->setJavaScriptEnabled(true);
+ s->setDefaultTextEncodingName("latin1");
+ s->setPluginsEnabled(false);
+ s->setShrinksStandaloneImagesToFit(false);
+#ifdef ANDROID_LAYOUT
+ s->setUseWideViewport(false);
+#endif
+
+ // Finally, load the actual data
+ ResourceRequest req(url);
+ frame->loader()->load(req, false);
+
+ do {
+ // Layout the page and service the timer
+ frame->view()->layout();
+ while (client.m_hasTimer) {
+ client.m_func();
+ JavaSharedClient::ServiceFunctionPtrQueue();
+ }
+ JavaSharedClient::ServiceFunctionPtrQueue();
+
+ // Layout more if needed.
+ while (frame->view()->needsLayout())
+ frame->view()->layout();
+ JavaSharedClient::ServiceFunctionPtrQueue();
+
+ if (reloadCount)
+ frame->loader()->reload(true);
+ } while (reloadCount--);
+
+ // Draw into an offscreen bitmap
+ SkBitmap bmp;
+ bmp.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+ bmp.allocPixels();
+ SkCanvas canvas(bmp);
+ PlatformGraphicsContext ctx(&canvas, NULL);
+ GraphicsContext gc(&ctx);
+ frame->view()->paintContents(&gc, IntRect(0, 0, width, height));
+
+ // Write the bitmap to the sdcard
+ SkImageEncoder* enc = SkImageEncoder::Create(SkImageEncoder::kPNG_Type);
+ enc->encodeFile("/sdcard/webcore_test.png", bmp, 100);
+ delete enc;
+
+ // Tear down the world.
+ frame->loader()->detachFromParent();
+ delete page;
+}
+
+} // namespace android
diff --git a/Source/WebKit/android/jni/WebCoreRefObject.h b/Source/WebKit/android/jni/WebCoreRefObject.h
new file mode 100644
index 0000000..4228db6
--- /dev/null
+++ b/Source/WebKit/android/jni/WebCoreRefObject.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2007, 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.
+ */
+
+#ifndef WEBCORE_FOUNDATION_h
+#define WEBCORE_FOUNDATION_h
+
+#include "SkRefCnt.h"
+
+typedef SkRefCnt WebCoreRefObject;
+
+static inline WebCoreRefObject* Retain(WebCoreRefObject* obj)
+{
+ if (obj)
+ obj->ref();
+ return obj;
+}
+
+static inline void Release(WebCoreRefObject* obj)
+{
+ if (obj)
+ obj->unref();
+}
+
+#endif // WEBCORE_FOUNDATION_h
diff --git a/Source/WebKit/android/jni/WebCoreResourceLoader.cpp b/Source/WebKit/android/jni/WebCoreResourceLoader.cpp
new file mode 100644
index 0000000..f9acc97
--- /dev/null
+++ b/Source/WebKit/android/jni/WebCoreResourceLoader.cpp
@@ -0,0 +1,352 @@
+/*
+ * 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 "WebCoreResourceLoader.h"
+
+#include "ResourceError.h"
+#include "ResourceHandle.h"
+#include "ResourceHandleClient.h"
+#include "ResourceHandleInternal.h"
+#include "ResourceResponse.h"
+#include "SkUtils.h"
+#ifdef ANDROID_INSTRUMENT
+#include "TimeCounter.h"
+#endif
+#include "WebCoreJni.h"
+
+#include <JNIHelp.h>
+#include <JNIUtility.h>
+#include <SkTypes.h>
+#include <stdlib.h>
+#include <utils/misc.h>
+#include <wtf/Platform.h>
+#include <wtf/text/CString.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+static struct resourceloader_t {
+ jfieldID mObject;
+ jmethodID mCancelMethodID;
+ jmethodID mDownloadFileMethodID;
+ jmethodID mWillLoadFromCacheMethodID;
+ jmethodID mPauseLoadMethodID;
+} gResourceLoader;
+
+// ----------------------------------------------------------------------------
+
+#define GET_NATIVE_HANDLE(env, obj) ((WebCore::ResourceHandle*)env->GetIntField(obj, gResourceLoader.mObject))
+#define SET_NATIVE_HANDLE(env, obj, handle) (env->SetIntField(obj, gResourceLoader.mObject, handle))
+
+//-----------------------------------------------------------------------------
+// ResourceLoadHandler
+
+PassRefPtr<WebCore::ResourceLoaderAndroid> WebCoreResourceLoader::create(JNIEnv *env, jobject jLoadListener)
+{
+ return adoptRef<WebCore::ResourceLoaderAndroid>(new WebCoreResourceLoader(env, jLoadListener));
+}
+
+WebCoreResourceLoader::WebCoreResourceLoader(JNIEnv *env, jobject jLoadListener)
+ : mPausedLoad(false)
+{
+ mJLoader = env->NewGlobalRef(jLoadListener);
+}
+
+WebCoreResourceLoader::~WebCoreResourceLoader()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ SET_NATIVE_HANDLE(env, mJLoader, 0);
+ env->DeleteGlobalRef(mJLoader);
+ mJLoader = 0;
+}
+
+void WebCoreResourceLoader::cancel()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(mJLoader, gResourceLoader.mCancelMethodID);
+ checkException(env);
+}
+
+void WebCoreResourceLoader::downloadFile()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(mJLoader, gResourceLoader.mDownloadFileMethodID);
+ checkException(env);
+}
+
+void WebCoreResourceLoader::pauseLoad(bool pause)
+{
+ if (mPausedLoad == pause)
+ return;
+
+ mPausedLoad = pause;
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(mJLoader, gResourceLoader.mPauseLoadMethodID, pause);
+ checkException(env);
+}
+
+/*
+* This static method is called to check to see if a POST response is in
+* the cache. This may be slow, but is only used during a navigation to
+* a POST response.
+*/
+bool WebCoreResourceLoader::willLoadFromCache(const WebCore::KURL& url, int64_t identifier)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ WTF::String urlStr = url.string();
+ jstring jUrlStr = wtfStringToJstring(env, urlStr);
+ jclass resourceLoader = env->FindClass("android/webkit/LoadListener");
+ bool val = env->CallStaticBooleanMethod(resourceLoader, gResourceLoader.mWillLoadFromCacheMethodID, jUrlStr, identifier);
+ checkException(env);
+ env->DeleteLocalRef(resourceLoader);
+ env->DeleteLocalRef(jUrlStr);
+
+ return val;
+}
+
+// ----------------------------------------------------------------------------
+void WebCoreResourceLoader::SetResponseHeader(JNIEnv* env, jobject obj, jint nativeResponse, jstring key, jstring val)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::ResourceTimeCounter);
+#endif
+
+ WebCore::ResourceResponse* response = (WebCore::ResourceResponse*)nativeResponse;
+ LOG_ASSERT(response, "nativeSetResponseHeader must take a valid response pointer!");
+
+ LOG_ASSERT(key, "How did a null value become a key?");
+ if (val)
+ response->setHTTPHeaderField(jstringToWtfString(env, key), jstringToWtfString(env, val));
+}
+
+jint WebCoreResourceLoader::CreateResponse(JNIEnv* env, jobject obj, jstring url, jint statusCode,
+ jstring statusText, jstring mimeType, jlong expectedLength,
+ jstring encoding)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::ResourceTimeCounter);
+#endif
+ LOG_ASSERT(url, "Must have a url in the response!");
+ WebCore::KURL kurl(WebCore::ParsedURLString, jstringToWtfString(env, url));
+ WTF::String encodingStr;
+ WTF::String mimeTypeStr;
+ if (mimeType) {
+ mimeTypeStr = jstringToWtfString(env, mimeType);
+ LOGV("Response setMIMEType: %s", mimeTypeStr.latin1().data());
+ }
+ if (encoding) {
+ encodingStr = jstringToWtfString(env, encoding);
+ LOGV("Response setTextEncodingName: %s", encodingStr.latin1().data());
+ }
+ WebCore::ResourceResponse* response = new WebCore::ResourceResponse(
+ kurl, mimeTypeStr, (long long)expectedLength,
+ encodingStr, WTF::String());
+ response->setHTTPStatusCode(statusCode);
+ if (statusText) {
+ WTF::String status = jstringToWtfString(env, statusText);
+ response->setHTTPStatusText(status);
+ LOGV("Response setStatusText: %s", status.latin1().data());
+ }
+ return (int)response;
+}
+
+void WebCoreResourceLoader::ReceivedResponse(JNIEnv* env, jobject obj, jint nativeResponse)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::ResourceTimeCounter);
+#endif
+ WebCore::ResourceHandle* handle = GET_NATIVE_HANDLE(env, obj);
+ LOG_ASSERT(handle, "nativeReceivedResponse must take a valid handle!");
+ // ResourceLoader::didFail() can set handle to be NULL, we need to check
+ if (!handle)
+ return;
+
+ WebCore::ResourceResponse* response = (WebCore::ResourceResponse*)nativeResponse;
+ LOG_ASSERT(response, "nativeReceivedResponse must take a valid resource pointer!");
+ handle->client()->didReceiveResponse(handle, *response);
+ // As the client makes a copy of the response, delete it here.
+ delete response;
+}
+
+void WebCoreResourceLoader::AddData(JNIEnv* env, jobject obj, jbyteArray dataArray, jint length)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::ResourceTimeCounter);
+#endif
+ LOGV("webcore_resourceloader data(%d)", length);
+
+ WebCore::ResourceHandle* handle = GET_NATIVE_HANDLE(env, obj);
+ LOG_ASSERT(handle, "nativeAddData must take a valid handle!");
+ // ResourceLoader::didFail() can set handle to be NULL, we need to check
+ if (!handle)
+ return;
+
+ SkAutoMemoryUsageProbe mup("android_webcore_resourceloader_nativeAddData");
+
+ bool result = false;
+ jbyte * data = env->GetByteArrayElements(dataArray, NULL);
+
+ LOG_ASSERT(handle->client(), "Why do we not have a client?");
+ handle->client()->didReceiveData(handle, (const char *)data, length, length);
+ env->ReleaseByteArrayElements(dataArray, data, JNI_ABORT);
+}
+
+void WebCoreResourceLoader::Finished(JNIEnv* env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::ResourceTimeCounter);
+#endif
+ LOGV("webcore_resourceloader finished");
+ WebCore::ResourceHandle* handle = GET_NATIVE_HANDLE(env, obj);
+ LOG_ASSERT(handle, "nativeFinished must take a valid handle!");
+ // ResourceLoader::didFail() can set handle to be NULL, we need to check
+ if (!handle)
+ return;
+
+ LOG_ASSERT(handle->client(), "Why do we not have a client?");
+ handle->client()->didFinishLoading(handle, 0);
+}
+
+jstring WebCoreResourceLoader::RedirectedToUrl(JNIEnv* env, jobject obj,
+ jstring baseUrl, jstring redirectTo, jint nativeResponse)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::ResourceTimeCounter);
+#endif
+ LOGV("webcore_resourceloader redirectedToUrl");
+ WebCore::ResourceHandle* handle = GET_NATIVE_HANDLE(env, obj);
+ LOG_ASSERT(handle, "nativeRedirectedToUrl must take a valid handle!");
+ // ResourceLoader::didFail() can set handle to be NULL, we need to check
+ if (!handle)
+ return NULL;
+
+ LOG_ASSERT(handle->client(), "Why do we not have a client?");
+ WebCore::ResourceRequest r = handle->firstRequest();
+ WebCore::KURL url(WebCore::KURL(WebCore::ParsedURLString, jstringToWtfString(env, baseUrl)),
+ jstringToWtfString(env, redirectTo));
+ WebCore::ResourceResponse* response = (WebCore::ResourceResponse*)nativeResponse;
+ // If the url fails to resolve the relative path, return null.
+ if (url.protocol().isEmpty()) {
+ delete response;
+ return NULL;
+ } else {
+ // Ensure the protocol is lowercase.
+ url.setProtocol(url.protocol().lower());
+ }
+ // Set the url after updating the protocol.
+ r.setURL(url);
+ if (r.httpMethod() == "POST") {
+ r.setHTTPMethod("GET");
+ r.clearHTTPReferrer();
+ r.setHTTPBody(0);
+ r.setHTTPContentType("");
+ }
+ handle->client()->willSendRequest(handle, r, *response);
+ delete response;
+ return wtfStringToJstring(env, url.string());
+}
+
+void WebCoreResourceLoader::Error(JNIEnv* env, jobject obj, jint id, jstring description,
+ jstring failingUrl)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::ResourceTimeCounter);
+#endif
+ LOGV("webcore_resourceloader error");
+ WebCore::ResourceHandle* handle = GET_NATIVE_HANDLE(env, obj);
+ LOG_ASSERT(handle, "nativeError must take a valid handle!");
+ // ResourceLoader::didFail() can set handle to be NULL, we need to check
+ if (!handle)
+ return;
+
+ handle->client()->didFail(handle, WebCore::ResourceError("", id,
+ jstringToWtfString(env, failingUrl), jstringToWtfString(env, description)));
+}
+
+// ----------------------------------------------------------------------------
+
+/*
+ * JNI registration.
+ */
+static JNINativeMethod gResourceloaderMethods[] = {
+ /* name, signature, funcPtr */
+ { "nativeSetResponseHeader", "(ILjava/lang/String;Ljava/lang/String;)V",
+ (void*) WebCoreResourceLoader::SetResponseHeader },
+ { "nativeCreateResponse", "(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;JLjava/lang/String;)I",
+ (void*) WebCoreResourceLoader::CreateResponse },
+ { "nativeReceivedResponse", "(I)V",
+ (void*) WebCoreResourceLoader::ReceivedResponse },
+ { "nativeAddData", "([BI)V",
+ (void*) WebCoreResourceLoader::AddData },
+ { "nativeFinished", "()V",
+ (void*) WebCoreResourceLoader::Finished },
+ { "nativeRedirectedToUrl", "(Ljava/lang/String;Ljava/lang/String;I)Ljava/lang/String;",
+ (void*) WebCoreResourceLoader::RedirectedToUrl },
+ { "nativeError", "(ILjava/lang/String;Ljava/lang/String;)V",
+ (void*) WebCoreResourceLoader::Error }
+};
+
+int registerResourceLoader(JNIEnv* env)
+{
+ jclass resourceLoader = env->FindClass("android/webkit/LoadListener");
+ LOG_FATAL_IF(resourceLoader == NULL,
+ "Unable to find class android/webkit/LoadListener");
+
+ gResourceLoader.mObject =
+ env->GetFieldID(resourceLoader, "mNativeLoader", "I");
+ LOG_FATAL_IF(gResourceLoader.mObject == NULL,
+ "Unable to find android/webkit/LoadListener.mNativeLoader");
+
+ gResourceLoader.mCancelMethodID =
+ env->GetMethodID(resourceLoader, "cancel", "()V");
+ LOG_FATAL_IF(gResourceLoader.mCancelMethodID == NULL,
+ "Could not find method cancel on LoadListener");
+
+ gResourceLoader.mDownloadFileMethodID =
+ env->GetMethodID(resourceLoader, "downloadFile", "()V");
+ LOG_FATAL_IF(gResourceLoader.mDownloadFileMethodID == NULL,
+ "Could not find method downloadFile on LoadListener");
+
+ gResourceLoader.mPauseLoadMethodID =
+ env->GetMethodID(resourceLoader, "pauseLoad", "(Z)V");
+ LOG_FATAL_IF(gResourceLoader.mPauseLoadMethodID == NULL,
+ "Could not find method pauseLoad on LoadListener");
+
+ gResourceLoader.mWillLoadFromCacheMethodID =
+ env->GetStaticMethodID(resourceLoader, "willLoadFromCache", "(Ljava/lang/String;J)Z");
+ LOG_FATAL_IF(gResourceLoader.mWillLoadFromCacheMethodID == NULL,
+ "Could not find static method willLoadFromCache on LoadListener");
+
+ env->DeleteLocalRef(resourceLoader);
+
+ return jniRegisterNativeMethods(env, "android/webkit/LoadListener",
+ gResourceloaderMethods, NELEM(gResourceloaderMethods));
+}
+
+} /* namespace android */
diff --git a/Source/WebKit/android/jni/WebCoreResourceLoader.h b/Source/WebKit/android/jni/WebCoreResourceLoader.h
new file mode 100644
index 0000000..c60b3f5
--- /dev/null
+++ b/Source/WebKit/android/jni/WebCoreResourceLoader.h
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_WEBKIT_RESOURCELOADLISTENER_H
+#define ANDROID_WEBKIT_RESOURCELOADLISTENER_H
+
+#include <KURL.h>
+#include <ResourceLoaderAndroid.h>
+#include <jni.h>
+
+namespace android {
+
+class WebCoreResourceLoader : public WebCore::ResourceLoaderAndroid
+{
+public:
+ static PassRefPtr<WebCore::ResourceLoaderAndroid> create(JNIEnv *env, jobject jLoadListener);
+ virtual ~WebCoreResourceLoader();
+
+ /**
+ * Call to java to cancel the current load.
+ */
+ virtual void cancel();
+
+ /**
+ * Call to java to download the current load rather than feed it
+ * back to WebCore
+ */
+ virtual void downloadFile();
+
+ virtual void pauseLoad(bool);
+
+ /**
+ * Call to java to find out if this URL is in the cache
+ */
+ static bool willLoadFromCache(const WebCore::KURL& url, int64_t identifier);
+
+ // Native jni functions
+ static void SetResponseHeader(JNIEnv*, jobject, jint, jstring, jstring);
+ static jint CreateResponse(JNIEnv*, jobject, jstring, jint, jstring,
+ jstring, jlong, jstring);
+ static void ReceivedResponse(JNIEnv*, jobject, jint);
+ static void AddData(JNIEnv*, jobject, jbyteArray, jint);
+ static void Finished(JNIEnv*, jobject);
+ static jstring RedirectedToUrl(JNIEnv*, jobject, jstring, jstring, jint);
+ static void Error(JNIEnv*, jobject, jint, jstring, jstring);
+
+protected:
+ WebCoreResourceLoader(JNIEnv *env, jobject jLoadListener);
+private:
+ jobject mJLoader;
+ bool mPausedLoad;
+};
+
+} // end namespace android
+
+#endif
diff --git a/Source/WebKit/android/jni/WebCoreViewBridge.h b/Source/WebKit/android/jni/WebCoreViewBridge.h
new file mode 100644
index 0000000..59e1c9a
--- /dev/null
+++ b/Source/WebKit/android/jni/WebCoreViewBridge.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2007, 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.
+ */
+
+#ifndef WEBCORE_VIEW_BRIDGE_H
+#define WEBCORE_VIEW_BRIDGE_H
+
+// TODO: move this outside of jni directory
+
+#include "IntRect.h"
+#include "WebCoreRefObject.h"
+
+namespace WebCore
+{
+ class GraphicsContext;
+}
+
+class WebCoreViewBridge : public WebCoreRefObject {
+public:
+ WebCoreViewBridge() { }
+ virtual ~WebCoreViewBridge() { }
+
+ virtual void draw(WebCore::GraphicsContext* ctx,
+ const WebCore::IntRect& rect) = 0;
+
+ const WebCore::IntRect& getBounds() const
+ {
+ return m_bounds;
+ }
+
+ const WebCore::IntRect& getVisibleBounds() const
+ {
+ return m_visibleBounds;
+ }
+
+ const WebCore::IntRect& getWindowBounds() const
+ {
+ return m_windowBounds;
+ }
+
+ void setSize(int w, int h)
+ {
+ m_bounds.setWidth(w);
+ m_bounds.setHeight(h);
+ }
+
+ void setVisibleSize(int w, int h)
+ {
+ m_visibleBounds.setWidth(w);
+ m_visibleBounds.setHeight(h);
+ }
+
+ void setLocation(int x, int y)
+ {
+ m_bounds.setX(x);
+ m_bounds.setY(y);
+ m_visibleBounds.setX(x);
+ m_visibleBounds.setY(y);
+ }
+
+ void setWindowBounds(int x, int y, int h, int v)
+ {
+ m_windowBounds = WebCore::IntRect(x, y, h, v);
+ }
+
+ int width() const { return m_bounds.width(); }
+ int height() const { return m_bounds.height(); }
+ int locX() const { return m_bounds.x(); }
+ int locY() const { return m_bounds.y(); }
+
+ int visibleWidth() const { return m_visibleBounds.width(); }
+ int visibleHeight() const { return m_visibleBounds.height(); }
+ int visibleX() const { return m_visibleBounds.x(); }
+ int visibleY() const { return m_visibleBounds.y(); }
+
+ virtual bool forFrameView() const { return false; }
+ virtual bool forPluginView() const { return false; }
+
+private:
+ WebCore::IntRect m_bounds;
+ WebCore::IntRect m_windowBounds;
+ WebCore::IntRect m_visibleBounds;
+};
+
+#endif // WEBCORE_VIEW_BRIDGE_H
diff --git a/Source/WebKit/android/jni/WebFrameView.cpp b/Source/WebKit/android/jni/WebFrameView.cpp
new file mode 100644
index 0000000..8e5eac4
--- /dev/null
+++ b/Source/WebKit/android/jni/WebFrameView.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2008, 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 "WebFrameView.h"
+
+#include "android_graphics.h"
+#include "GraphicsContext.h"
+#include "Frame.h"
+#include "FrameTree.h"
+#include "FrameView.h"
+#include "HostWindow.h"
+#include "PlatformGraphicsContext.h"
+#include "WebViewCore.h"
+
+#include <SkCanvas.h>
+
+namespace android {
+
+WebFrameView::WebFrameView(WebCore::FrameView* frameView, WebViewCore* webViewCore)
+ : WebCoreViewBridge()
+ , mFrameView(frameView)
+ , mWebViewCore(webViewCore) {
+ // attach itself to mFrameView
+ mFrameView->setPlatformWidget(this);
+ Retain(mWebViewCore);
+}
+
+WebFrameView::~WebFrameView() {
+ Release(mWebViewCore);
+}
+
+void WebFrameView::draw(WebCore::GraphicsContext* ctx, const WebCore::IntRect& rect) {
+ WebCore::Frame* frame = mFrameView->frame();
+
+ if (NULL == frame->contentRenderer()) {
+ // We only do this if there is nothing else to draw.
+ // If there is a renderer, it will fill the bg itself, so we don't want to
+ // double-draw (slow)
+ SkCanvas* canvas = ctx->platformContext()->mCanvas;
+ canvas->drawColor(SK_ColorWHITE);
+ } else if (frame->tree()->parent()) {
+ // Note: this code was moved from FrameLoaderClientAndroid
+ //
+ // For subframe, create a new translated rect from the given rectangle.
+ WebCore::IntRect transRect(rect);
+ // In Frame::markAllMatchesForText(), it does a fake paint. So we need
+ // to handle the case where platformContext() is null. However, we still
+ // want to call paint, since WebKit must have called the paint for a reason.
+ SkCanvas* canvas = ctx->platformContext() ? ctx->platformContext()->mCanvas : NULL;
+ if (canvas) {
+ const WebCore::IntRect& bounds = getBounds();
+
+ // Grab the intersection of transRect and the frame's bounds.
+ transRect.intersect(bounds);
+ if (transRect.isEmpty())
+ return;
+
+ // Move the transRect into the frame's local coordinates.
+ transRect.move(-bounds.x(), -bounds.y());
+
+ // Translate the canvas, add a clip.
+ canvas->save();
+ canvas->translate(SkIntToScalar(bounds.x()), SkIntToScalar(bounds.y()));
+ canvas->clipRect(transRect);
+ }
+ mFrameView->paintContents(ctx, transRect);
+ if (canvas)
+ canvas->restore();
+ } else {
+ mFrameView->paintContents(ctx, rect);
+ }
+}
+
+void WebFrameView::setView(WebCore::FrameView* frameView) {
+ mFrameView = frameView;
+ mFrameView->setPlatformWidget(this);
+}
+
+} // namespace android
diff --git a/Source/WebKit/android/jni/WebFrameView.h b/Source/WebKit/android/jni/WebFrameView.h
new file mode 100644
index 0000000..823f2b4
--- /dev/null
+++ b/Source/WebKit/android/jni/WebFrameView.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2008, 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.
+ */
+
+#ifndef WEB_FRAMEVIEW_H
+#define WEB_FRAMEVIEW_H
+
+#include "WebCoreViewBridge.h"
+
+namespace WebCore {
+ class FrameView;
+}
+
+namespace android {
+ class WebViewCore;
+
+ class WebFrameView: public WebCoreViewBridge {
+ public:
+ WebFrameView(WebCore::FrameView* frameView, WebViewCore* webViewCore);
+ virtual ~WebFrameView();
+
+ virtual void draw(WebCore::GraphicsContext* ctx,
+ const WebCore::IntRect& rect);
+
+ WebViewCore* webViewCore() const {
+ return mWebViewCore;
+ }
+
+ void setView(WebCore::FrameView* frameView);
+
+ WebCore::FrameView* view() const {
+ return mFrameView;
+ }
+
+ virtual bool forFrameView() const { return true; }
+
+ private:
+ WebCore::FrameView* mFrameView;
+ WebViewCore* mWebViewCore;
+ };
+
+} // namespace android
+
+#endif // WEB_FRAMEVIEW_H
diff --git a/Source/WebKit/android/jni/WebHistory.cpp b/Source/WebKit/android/jni/WebHistory.cpp
new file mode 100644
index 0000000..97ce23b
--- /dev/null
+++ b/Source/WebKit/android/jni/WebHistory.cpp
@@ -0,0 +1,857 @@
+/*
+ * Copyright 2007, 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 "webhistory"
+
+#include "config.h"
+#include "WebHistory.h"
+
+#include "BackForwardList.h"
+#include "BackForwardListImpl.h"
+#include "DocumentLoader.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameLoaderClientAndroid.h"
+#include "FrameTree.h"
+#include "HistoryItem.h"
+#include "IconDatabase.h"
+#include "Page.h"
+#include "TextEncoding.h"
+#include "WebCoreFrameBridge.h"
+#include "WebCoreJni.h"
+#include "WebIconDatabase.h"
+
+#include <JNIHelp.h>
+#include "JNIUtility.h"
+#include <SkUtils.h>
+#include <utils/misc.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/Platform.h>
+#include <wtf/text/CString.h>
+
+namespace android {
+
+// Forward declarations
+static void write_item(WTF::Vector<char>& v, WebCore::HistoryItem* item);
+static void write_children_recursive(WTF::Vector<char>& v, WebCore::HistoryItem* parent);
+static bool read_item_recursive(WebCore::HistoryItem* child, const char** pData, int length);
+
+// Field ids for WebHistoryItems
+struct WebHistoryItemFields {
+ jmethodID mInit;
+ jmethodID mUpdate;
+ jfieldID mTitle;
+ jfieldID mUrl;
+} gWebHistoryItem;
+
+struct WebBackForwardListFields {
+ jmethodID mAddHistoryItem;
+ jmethodID mRemoveHistoryItem;
+ jmethodID mSetCurrentIndex;
+} gWebBackForwardList;
+
+//--------------------------------------------------------------------------
+// WebBackForwardList native methods.
+//--------------------------------------------------------------------------
+
+static void WebHistoryClose(JNIEnv* env, jobject obj, jint frame)
+{
+ LOG_ASSERT(frame, "Close needs a valid Frame pointer!");
+ WebCore::Frame* pFrame = (WebCore::Frame*)frame;
+
+ WebCore::BackForwardListImpl* list = static_cast<WebCore::BackForwardListImpl*>(pFrame->page()->backForwardList());
+ RefPtr<WebCore::HistoryItem> current = list->currentItem();
+ // Remove each item instead of using close(). close() is intended to be used
+ // right before the list is deleted.
+ WebCore::HistoryItemVector& entries = list->entries();
+ int size = entries.size();
+ for (int i = size - 1; i >= 0; --i)
+ list->removeItem(entries[i].get());
+ // Add the current item back to the list.
+ if (current) {
+ current->setBridge(0);
+ // addItem will update the children to match the newly created bridge
+ list->addItem(current);
+
+ /*
+ * The Grand Prix site uses anchor navigations to change the display.
+ * WebKit tries to be smart and not load child frames that have the
+ * same history urls during an anchor navigation. This means that the
+ * current history item stored in the child frame's loader does not
+ * match the item found in the history tree. If we remove all the
+ * entries in the back/foward list, we have to restore the entire tree
+ * or else a HistoryItem might have a deleted parent.
+ *
+ * In order to restore the history tree correctly, we have to look up
+ * all the frames first and then look up the history item. We do this
+ * because the history item in the tree may be null at this point.
+ * Unfortunately, a HistoryItem can only search its immediately
+ * children so we do a breadth-first rebuild of the tree.
+ */
+
+ // Keep a small list of child frames to traverse.
+ WTF::Vector<WebCore::Frame*> frameQueue;
+ // Fix the top-level item.
+ pFrame->loader()->history()->setCurrentItem(current.get());
+ WebCore::Frame* child = pFrame->tree()->firstChild();
+ // Remember the parent history item so we can search for a child item.
+ RefPtr<WebCore::HistoryItem> parent = current;
+ while (child) {
+ // Use the old history item since the current one may have a
+ // deleted parent.
+ WebCore::HistoryItem* item = parent->childItemWithTarget(child->tree()->name());
+ child->loader()->history()->setCurrentItem(item);
+ // Append the first child to the queue if it exists. If there is no
+ // item, then we do not need to traverse the children since there
+ // will be no parent history item.
+ WebCore::Frame* firstChild;
+ if (item && (firstChild = child->tree()->firstChild()))
+ frameQueue.append(firstChild);
+ child = child->tree()->nextSibling();
+ // If we don't have a sibling for this frame and the queue isn't
+ // empty, use the next entry in the queue.
+ if (!child && !frameQueue.isEmpty()) {
+ child = frameQueue.at(0);
+ frameQueue.remove(0);
+ // Figure out the parent history item used when searching for
+ // the history item to use.
+ parent = child->tree()->parent()->loader()->history()->currentItem();
+ }
+ }
+ }
+}
+
+static void WebHistoryRestoreIndex(JNIEnv* env, jobject obj, jint frame, jint index)
+{
+ LOG_ASSERT(frame, "RestoreState needs a valid Frame pointer!");
+ WebCore::Frame* pFrame = (WebCore::Frame*)frame;
+ WebCore::Page* page = pFrame->page();
+ WebCore::HistoryItem* currentItem =
+ static_cast<WebCore::BackForwardListImpl*>(page->backForwardList())->entries()[index].get();
+
+ // load the current page with FrameLoadTypeIndexedBackForward so that it
+ // will use cache when it is possible
+ page->goToItem(currentItem, FrameLoadTypeIndexedBackForward);
+}
+
+static void WebHistoryInflate(JNIEnv* env, jobject obj, jint frame, jbyteArray data)
+{
+ LOG_ASSERT(frame, "Inflate needs a valid frame pointer!");
+ LOG_ASSERT(data, "Inflate needs a valid data pointer!");
+
+ // Get the actual bytes and the length from the java array.
+ const jbyte* bytes = env->GetByteArrayElements(data, NULL);
+ jsize size = env->GetArrayLength(data);
+
+ // Inflate the history tree into one HistoryItem or null if the inflation
+ // failed.
+ RefPtr<WebCore::HistoryItem> newItem = WebCore::HistoryItem::create();
+ WebHistoryItem* bridge = new WebHistoryItem(env, obj, newItem.get());
+ newItem->setBridge(bridge);
+
+ // Inflate the item recursively. If it fails, that is ok. We'll have an
+ // incomplete HistoryItem but that is better than crashing due to a null
+ // item.
+ // We have a 2nd local variable since read_item_recursive may change the
+ // ptr's value. We can't pass &bytes since we have to send bytes to
+ // ReleaseByteArrayElements unchanged.
+ const char* ptr = reinterpret_cast<const char*>(bytes);
+ read_item_recursive(newItem.get(), &ptr, (int)size);
+ env->ReleaseByteArrayElements(data, const_cast<jbyte*>(bytes), JNI_ABORT);
+ bridge->setActive();
+
+ // Add the new item to the back/forward list.
+ WebCore::Frame* pFrame = (WebCore::Frame*)frame;
+ pFrame->page()->backForwardList()->addItem(newItem);
+
+ // Update the item.
+ bridge->updateHistoryItem(newItem.get());
+}
+
+// 6 empty strings + no document state + children count + 2 scales = 10 unsigned values
+// 1 char for isTargetItem.
+#define HISTORY_MIN_SIZE ((int)(sizeof(unsigned) * 10 + sizeof(char)))
+
+jbyteArray WebHistory::Flatten(JNIEnv* env, WTF::Vector<char>& v, WebCore::HistoryItem* item)
+{
+ if (!item)
+ return NULL;
+
+ // Reserve a vector of chars with an initial size of HISTORY_MIN_SIZE.
+ v.reserveCapacity(HISTORY_MIN_SIZE);
+
+ // Write the top-level history item and then write all the children
+ // recursively.
+ LOG_ASSERT(item->bridge(), "Why don't we have a bridge object here?");
+ write_item(v, item);
+ write_children_recursive(v, item);
+
+ // Try to create a new java byte array.
+ jbyteArray b = env->NewByteArray(v.size());
+ if (!b)
+ return NULL;
+
+ // Write our flattened data to the java array.
+ env->SetByteArrayRegion(b, 0, v.size(), (const jbyte*)v.data());
+ return b;
+}
+
+WebHistoryItem::WebHistoryItem(JNIEnv* env, jobject obj,
+ WebCore::HistoryItem* item) : WebCore::AndroidWebHistoryBridge(item) {
+ m_object = env->NewWeakGlobalRef(obj);
+ m_parent = 0;
+}
+
+WebHistoryItem::~WebHistoryItem() {
+ if (m_object) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (!env)
+ return;
+ env->DeleteWeakGlobalRef(m_object);
+ }
+}
+
+void WebHistoryItem::updateHistoryItem(WebCore::HistoryItem* item) {
+ // Do not want to update during inflation.
+ if (!m_active)
+ return;
+ WebHistoryItem* webItem = this;
+ // Now we need to update the top-most WebHistoryItem based on the top-most
+ // HistoryItem.
+ if (m_parent) {
+ webItem = m_parent.get();
+ if (webItem->hasOneRef()) {
+ // if the parent only has one ref, it is from this WebHistoryItem.
+ // This means that the matching WebCore::HistoryItem has been freed.
+ // This can happen during clear().
+ LOGW("Can't updateHistoryItem as the top HistoryItem is gone");
+ return;
+ }
+ while (webItem->parent())
+ webItem = webItem->parent();
+ item = webItem->historyItem();
+ if (!item) {
+ // If a HistoryItem only exists for page cache, it is possible that
+ // the parent HistoryItem destroyed before the child HistoryItem. If
+ // it happens, skip updating.
+ LOGW("Can't updateHistoryItem as the top HistoryItem is gone");
+ return;
+ }
+ }
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (!env)
+ return;
+
+ // Don't do anything if the item has been gc'd already
+ AutoJObject realItem = getRealObject(env, webItem->m_object);
+ if (!realItem.get())
+ return;
+
+ const WTF::String& urlString = item->urlString();
+ jstring urlStr = NULL;
+ if (!urlString.isNull())
+ urlStr = wtfStringToJstring(env, urlString);
+ const WTF::String& originalUrlString = item->originalURLString();
+ jstring originalUrlStr = NULL;
+ if (!originalUrlString.isNull())
+ originalUrlStr = wtfStringToJstring(env, originalUrlString);
+ const WTF::String& titleString = item->title();
+ jstring titleStr = NULL;
+ if (!titleString.isNull())
+ titleStr = wtfStringToJstring(env, titleString);
+
+ // Try to get the favicon from the history item. For some pages like Grand
+ // Prix, there are history items with anchors. If the icon fails for the
+ // item, try to get the icon using the url without the ref.
+ jobject favicon = NULL;
+ WTF::String url = item->urlString();
+ if (item->url().hasFragmentIdentifier()) {
+ int refIndex = url.reverseFind('#');
+ url = url.substring(0, refIndex);
+ }
+ WebCore::Image* icon = WebCore::iconDatabase()->iconForPageURL(url,
+ WebCore::IntSize(16, 16));
+
+ if (icon)
+ favicon = webcoreImageToJavaBitmap(env, icon);
+
+ WTF::Vector<char> data;
+ jbyteArray array = WebHistory::Flatten(env, data, item);
+ env->CallVoidMethod(realItem.get(), gWebHistoryItem.mUpdate, urlStr,
+ originalUrlStr, titleStr, favicon, array);
+ env->DeleteLocalRef(urlStr);
+ env->DeleteLocalRef(originalUrlStr);
+ env->DeleteLocalRef(titleStr);
+ if (favicon)
+ env->DeleteLocalRef(favicon);
+ env->DeleteLocalRef(array);
+}
+
+static void historyItemChanged(WebCore::HistoryItem* item) {
+ LOG_ASSERT(item, "historyItemChanged called with a null item");
+
+ if (item->bridge())
+ item->bridge()->updateHistoryItem(item);
+}
+
+void WebHistory::AddItem(const AutoJObject& list, WebCore::HistoryItem* item)
+{
+ LOG_ASSERT(item, "newItem must take a valid HistoryItem!");
+ // Item already added. Should only happen when we are inflating the list.
+ if (item->bridge() || !list.get())
+ return;
+
+ JNIEnv* env = list.env();
+ // Allocate a blank WebHistoryItem
+ jclass clazz = env->FindClass("android/webkit/WebHistoryItem");
+ jobject newItem = env->NewObject(clazz, gWebHistoryItem.mInit);
+ env->DeleteLocalRef(clazz);
+
+ // Create the bridge, make it active, and attach it to the item.
+ WebHistoryItem* bridge = new WebHistoryItem(env, newItem, item);
+ bridge->setActive();
+ item->setBridge(bridge);
+
+ // Update the history item which will flatten the data and call update on
+ // the java item.
+ bridge->updateHistoryItem(item);
+
+ // Add it to the list.
+ env->CallVoidMethod(list.get(), gWebBackForwardList.mAddHistoryItem, newItem);
+
+ // Delete our local reference.
+ env->DeleteLocalRef(newItem);
+}
+
+void WebHistory::RemoveItem(const AutoJObject& list, int index)
+{
+ if (list.get())
+ list.env()->CallVoidMethod(list.get(), gWebBackForwardList.mRemoveHistoryItem, index);
+}
+
+void WebHistory::UpdateHistoryIndex(const AutoJObject& list, int newIndex)
+{
+ if (list.get())
+ list.env()->CallVoidMethod(list.get(), gWebBackForwardList.mSetCurrentIndex, newIndex);
+}
+
+static void write_string(WTF::Vector<char>& v, const WTF::String& str)
+{
+ unsigned strLen = str.length();
+ // Only do work if the string has data.
+ if (strLen) {
+ // Determine how much to grow the vector. Use the worst case for utf8 to
+ // avoid reading the string twice. Add sizeof(unsigned) to hold the
+ // string length in utf8.
+ unsigned vectorLen = v.size() + sizeof(unsigned);
+ unsigned length = (strLen << 2) + vectorLen;
+ // Grow the vector. This will change the value of v.size() but we
+ // remember the original size above.
+ v.grow(length);
+ // Grab the position to write to.
+ char* data = v.begin() + vectorLen;
+ // Write the actual string
+ int l = SkUTF16_ToUTF8(str.characters(), strLen, data);
+ LOGV("Writing string %d %.*s", l, l, data);
+ // Go back and write the utf8 length. Subtract sizeof(unsigned) from
+ // data to get the position to write the length.
+ memcpy(data - sizeof(unsigned), (char*)&l, sizeof(unsigned));
+ // Shrink the internal state of the vector so we match what was
+ // actually written.
+ v.shrink(vectorLen + l);
+ } else
+ v.append((char*)&strLen, sizeof(unsigned));
+}
+
+static void write_item(WTF::Vector<char>& v, WebCore::HistoryItem* item)
+{
+ // Original url
+ write_string(v, item->originalURLString());
+
+ // Url
+ write_string(v, item->urlString());
+
+ // Title
+ write_string(v, item->title());
+
+ // Form content type
+ write_string(v, item->formContentType());
+
+ // Form data
+ const WebCore::FormData* formData = item->formData();
+ if (formData) {
+ write_string(v, formData->flattenToString());
+ // save the identifier as it is not included in the flatten data
+ int64_t id = formData->identifier();
+ v.append((char*)&id, sizeof(int64_t));
+ } else
+ write_string(v, WTF::String()); // Empty constructor does not allocate a buffer.
+
+ // Target
+ write_string(v, item->target());
+
+ AndroidWebHistoryBridge* bridge = item->bridge();
+ LOG_ASSERT(bridge, "We should have a bridge here!");
+ // Screen scale
+ const float scale = bridge->scale();
+ LOGV("Writing scale %f", scale);
+ v.append((char*)&scale, sizeof(float));
+ const float textWrapScale = bridge->textWrapScale();
+ LOGV("Writing text wrap scale %f", textWrapScale);
+ v.append((char*)&textWrapScale, sizeof(float));
+
+ // Scroll position.
+ const int scrollX = item->scrollPoint().x();
+ v.append((char*)&scrollX, sizeof(int));
+ const int scrollY = item->scrollPoint().y();
+ v.append((char*)&scrollY, sizeof(int));
+
+ // Document state
+ const WTF::Vector<WTF::String>& docState = item->documentState();
+ WTF::Vector<WTF::String>::const_iterator end = docState.end();
+ unsigned stateSize = docState.size();
+ LOGV("Writing docState %d", stateSize);
+ v.append((char*)&stateSize, sizeof(unsigned));
+ for (WTF::Vector<WTF::String>::const_iterator i = docState.begin(); i != end; ++i) {
+ write_string(v, *i);
+ }
+
+ // Is target item
+ LOGV("Writing isTargetItem %d", item->isTargetItem());
+ v.append((char)item->isTargetItem());
+
+ // Children count
+ unsigned childCount = item->children().size();
+ LOGV("Writing childCount %d", childCount);
+ v.append((char*)&childCount, sizeof(unsigned));
+}
+
+static void write_children_recursive(WTF::Vector<char>& v, WebCore::HistoryItem* parent)
+{
+ const WebCore::HistoryItemVector& children = parent->children();
+ WebCore::HistoryItemVector::const_iterator end = children.end();
+ for (WebCore::HistoryItemVector::const_iterator i = children.begin(); i != end; ++i) {
+ WebCore::HistoryItem* item = (*i).get();
+ LOG_ASSERT(parent->bridge(),
+ "The parent item should have a bridge object!");
+ if (!item->bridge()) {
+ WebHistoryItem* bridge = new WebHistoryItem(static_cast<WebHistoryItem*>(parent->bridge()));
+ item->setBridge(bridge);
+ bridge->setActive();
+ } else {
+ // The only time this item's parent may not be the same as the
+ // parent's bridge is during history close. In that case, the
+ // parent must not have a parent bridge.
+ WebHistoryItem* bridge = static_cast<WebHistoryItem*>(item->bridge());
+ WebHistoryItem* parentBridge = static_cast<WebHistoryItem*>(parent->bridge());
+ LOG_ASSERT(parentBridge->parent() == 0 ||
+ bridge->parent() == parentBridge,
+ "Somehow this item has an incorrect parent");
+ bridge->setParent(parentBridge);
+ }
+ write_item(v, item);
+ write_children_recursive(v, item);
+ }
+}
+
+static bool read_item_recursive(WebCore::HistoryItem* newItem,
+ const char** pData, int length)
+{
+ if (!pData || length < HISTORY_MIN_SIZE)
+ return false;
+
+ const WebCore::TextEncoding& e = WebCore::UTF8Encoding();
+ const char* data = *pData;
+ const char* end = data + length;
+ int sizeofUnsigned = (int)sizeof(unsigned);
+
+ // Read the original url
+ // Read the expected length of the string.
+ int l;
+ memcpy(&l, data, sizeofUnsigned);
+ // Increment data pointer by the size of an unsigned int.
+ data += sizeofUnsigned;
+ if (l) {
+ LOGV("Original url %d %.*s", l, l, data);
+ // If we have a length, check if that length exceeds the data length
+ // and return null if there is not enough data.
+ if (data + l < end)
+ newItem->setOriginalURLString(e.decode(data, l));
+ else
+ return false;
+ // Increment the data pointer by the length of the string.
+ data += l;
+ }
+ // Check if we have enough data left to continue.
+ if (end - data < sizeofUnsigned)
+ return false;
+
+ // Read the url
+ memcpy(&l, data, sizeofUnsigned);
+ data += sizeofUnsigned;
+ if (l) {
+ LOGV("Url %d %.*s", l, l, data);
+ if (data + l < end)
+ newItem->setURLString(e.decode(data, l));
+ else
+ return false;
+ data += l;
+ }
+ if (end - data < sizeofUnsigned)
+ return false;
+
+ // Read the title
+ memcpy(&l, data, sizeofUnsigned);
+ data += sizeofUnsigned;
+ if (l) {
+ LOGV("Title %d %.*s", l, l, data);
+ if (data + l < end)
+ newItem->setTitle(e.decode(data, l));
+ else
+ return false;
+ data += l;
+ }
+ if (end - data < sizeofUnsigned)
+ return false;
+
+ // Generate a new ResourceRequest object for populating form information.
+ WTF::String formContentType;
+ WTF::PassRefPtr<WebCore::FormData> formData = NULL;
+
+ // Read the form content type
+ memcpy(&l, data, sizeofUnsigned);
+ data += sizeofUnsigned;
+ if (l) {
+ LOGV("Content type %d %.*s", l, l, data);
+ if (data + l < end)
+ formContentType = e.decode(data, l);
+ else
+ return false;
+ data += l;
+ }
+ if (end - data < sizeofUnsigned)
+ return false;
+
+ // Read the form data
+ memcpy(&l, data, sizeofUnsigned);
+ data += sizeofUnsigned;
+ if (l) {
+ LOGV("Form data %d %.*s", l, l, data);
+ if (data + l < end)
+ formData = WebCore::FormData::create(data, l);
+ else
+ return false;
+ data += l;
+ // Read the identifier
+ {
+ int64_t id;
+ int size = (int)sizeof(int64_t);
+ memcpy(&id, data, size);
+ data += size;
+ if (id)
+ formData->setIdentifier(id);
+ }
+ }
+ if (end - data < sizeofUnsigned)
+ return false;
+
+ // Set up the form info
+ if (formData != NULL) {
+ WebCore::ResourceRequest r;
+ r.setHTTPMethod("POST");
+ r.setHTTPContentType(formContentType);
+ r.setHTTPBody(formData);
+ newItem->setFormInfoFromRequest(r);
+ }
+
+ // Read the target
+ memcpy(&l, data, sizeofUnsigned);
+ data += sizeofUnsigned;
+ if (l) {
+ LOGV("Target %d %.*s", l, l, data);
+ if (data + l < end)
+ newItem->setTarget(e.decode(data, l));
+ else
+ return false;
+ data += l;
+ }
+ if (end - data < sizeofUnsigned)
+ return false;
+
+ AndroidWebHistoryBridge* bridge = newItem->bridge();
+ LOG_ASSERT(bridge, "There should be a bridge object during inflate");
+ float fValue;
+ // Read the screen scale
+ memcpy(&fValue, data, sizeof(float));
+ LOGV("Screen scale %f", fValue);
+ bridge->setScale(fValue);
+ data += sizeof(float);
+ memcpy(&fValue, data, sizeofUnsigned);
+ LOGV("Text wrap scale %f", fValue);
+ bridge->setTextWrapScale(fValue);
+ data += sizeof(float);
+
+ if (end - data < sizeofUnsigned)
+ return false;
+
+ // Read scroll position.
+ int scrollX = 0;
+ memcpy(&scrollX, data, sizeofUnsigned);
+ data += sizeofUnsigned;
+ int scrollY = 0;
+ memcpy(&scrollY, data, sizeofUnsigned);
+ data += sizeofUnsigned;
+ newItem->setScrollPoint(IntPoint(scrollX, scrollY));
+
+ if (end - data < sizeofUnsigned)
+ return false;
+
+ // Read the document state
+ memcpy(&l, data, sizeofUnsigned);
+ LOGV("Document state %d", l);
+ data += sizeofUnsigned;
+ if (l) {
+ // Check if we have enough data to at least parse the sizes of each
+ // document state string.
+ if (data + l * sizeofUnsigned >= end)
+ return false;
+ // Create a new vector and reserve enough space for the document state.
+ WTF::Vector<WTF::String> docState;
+ docState.reserveCapacity(l);
+ while (l--) {
+ // Check each time if we have enough to parse the length of the next
+ // string.
+ if (end - data < sizeofUnsigned)
+ return false;
+ int strLen;
+ memcpy(&strLen, data, sizeofUnsigned);
+ data += sizeofUnsigned;
+ if (data + strLen < end)
+ docState.append(e.decode(data, strLen));
+ else
+ return false;
+ LOGV("\t\t%d %.*s", strLen, strLen, data);
+ data += strLen;
+ }
+ newItem->setDocumentState(docState);
+ }
+ // Check if we have enough to read the next byte
+ if (data >= end)
+ return false;
+
+ // Read is target item
+ // Cast the value to unsigned char in order to make a negative value larger
+ // than 1. A value that is not 0 or 1 is a failure.
+ unsigned char c = (unsigned char)data[0];
+ if (c > 1)
+ return false;
+ LOGV("Target item %d", c);
+ newItem->setIsTargetItem((bool)c);
+ data++;
+ if (end - data < sizeofUnsigned)
+ return false;
+
+ // Read the child count
+ memcpy(&l, data, sizeofUnsigned);
+ LOGV("Child count %d", l);
+ data += sizeofUnsigned;
+ *pData = data;
+ if (l) {
+ // Check if we have the minimum amount need to parse l children.
+ if (data + l * HISTORY_MIN_SIZE >= end)
+ return false;
+ while (l--) {
+ // No need to check the length each time because read_item_recursive
+ // will return null if there isn't enough data left to parse.
+ WTF::PassRefPtr<WebCore::HistoryItem> child = WebCore::HistoryItem::create();
+ // Set a bridge that will not call into java.
+ child->setBridge(new WebHistoryItem(static_cast<WebHistoryItem*>(bridge)));
+ // Read the child item.
+ if (!read_item_recursive(child.get(), pData, end - data)) {
+ child.clear();
+ return false;
+ }
+ child->bridge()->setActive();
+ newItem->addChildItem(child);
+ }
+ }
+ return true;
+}
+
+// On arm, this test will cause memory corruption since converting char* will
+// byte align the result and this test does not use memset (it probably
+// should).
+// On the simulator, using HistoryItem will invoke the IconDatabase which will
+// initialize the main thread. Since this is invoked by the Zygote process, the
+// main thread will be incorrect and an assert will fire later.
+// In conclusion, define UNIT_TEST only if you know what you are doing.
+#ifdef UNIT_TEST
+static void unit_test()
+{
+ LOGD("Entering history unit test!");
+ const char* test1 = new char[0];
+ WTF::RefPtr<WebCore::HistoryItem> item = WebCore::HistoryItem::create();
+ WebCore::HistoryItem* testItem = item.get();
+ testItem->setBridge(new WebHistoryItem(0));
+ LOG_ASSERT(!read_item_recursive(testItem, &test1, 0), "0 length array should fail!");
+ delete[] test1;
+ const char* test2 = new char[2];
+ LOG_ASSERT(!read_item_recursive(testItem, &test2, 2), "Small array should fail!");
+ delete[] test2;
+ LOG_ASSERT(!read_item_recursive(testItem, NULL, HISTORY_MIN_SIZE), "Null data should fail!");
+ // Original Url
+ char* test3 = new char[HISTORY_MIN_SIZE];
+ const char* ptr = (const char*)test3;
+ memset(test3, 0, HISTORY_MIN_SIZE);
+ *(int*)test3 = 4000;
+ LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length originalUrl should fail!");
+ // Url
+ int offset = 4;
+ memset(test3, 0, HISTORY_MIN_SIZE);
+ ptr = (const char*)test3;
+ *(int*)(test3 + offset) = 4000;
+ LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length url should fail!");
+ // Title
+ offset += 4;
+ memset(test3, 0, HISTORY_MIN_SIZE);
+ ptr = (const char*)test3;
+ *(int*)(test3 + offset) = 4000;
+ LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length title should fail!");
+ // Form content type
+ offset += 4;
+ memset(test3, 0, HISTORY_MIN_SIZE);
+ ptr = (const char*)test3;
+ *(int*)(test3 + offset) = 4000;
+ LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length contentType should fail!");
+ // Form data
+ offset += 4;
+ memset(test3, 0, HISTORY_MIN_SIZE);
+ ptr = (const char*)test3;
+ *(int*)(test3 + offset) = 4000;
+ LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length form data should fail!");
+ // Target
+ offset += 4;
+ memset(test3, 0, HISTORY_MIN_SIZE);
+ ptr = (const char*)test3;
+ *(int*)(test3 + offset) = 4000;
+ LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length target should fail!");
+ offset += 4; // Scale
+ // Document state
+ offset += 4;
+ memset(test3, 0, HISTORY_MIN_SIZE);
+ ptr = (const char*)test3;
+ *(int*)(test3 + offset) = 4000;
+ LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length document state should fail!");
+ // Is target item
+ offset += 1;
+ memset(test3, 0, HISTORY_MIN_SIZE);
+ ptr = (const char*)test3;
+ *(char*)(test3 + offset) = '!';
+ LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "IsTargetItem should fail with ! as the value!");
+ // Child count
+ offset += 4;
+ memset(test3, 0, HISTORY_MIN_SIZE);
+ ptr = (const char*)test3;
+ *(int*)(test3 + offset) = 4000;
+ LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 kids should fail!");
+ offset = 36;
+ // Test document state
+ delete[] test3;
+ test3 = new char[HISTORY_MIN_SIZE + sizeof(unsigned)];
+ memset(test3, 0, HISTORY_MIN_SIZE + sizeof(unsigned));
+ ptr = (const char*)test3;
+ *(int*)(test3 + offset) = 1;
+ *(int*)(test3 + offset + 4) = 20;
+ LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE + sizeof(unsigned)), "1 20 length document state string should fail!");
+ delete[] test3;
+ test3 = new char[HISTORY_MIN_SIZE + 2 * sizeof(unsigned)];
+ memset(test3, 0, HISTORY_MIN_SIZE + 2 * sizeof(unsigned));
+ ptr = (const char*)test3;
+ *(int*)(test3 + offset) = 2;
+ *(int*)(test3 + offset + 4) = 0;
+ *(int*)(test3 + offset + 8) = 20;
+ LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE + 2 * sizeof(unsigned) ), "2 20 length document state string should fail!");
+ delete[] test3;
+}
+#endif
+
+//---------------------------------------------------------
+// JNI registration
+//---------------------------------------------------------
+static JNINativeMethod gWebBackForwardListMethods[] = {
+ { "nativeClose", "(I)V",
+ (void*) WebHistoryClose },
+ { "restoreIndex", "(II)V",
+ (void*) WebHistoryRestoreIndex }
+};
+
+static JNINativeMethod gWebHistoryItemMethods[] = {
+ { "inflate", "(I[B)V",
+ (void*) WebHistoryInflate }
+};
+
+int registerWebHistory(JNIEnv* env)
+{
+ // Get notified of all changes to history items.
+ WebCore::notifyHistoryItemChanged = historyItemChanged;
+#ifdef UNIT_TEST
+ unit_test();
+#endif
+ // Find WebHistoryItem, its constructor, and the update method.
+ jclass clazz = env->FindClass("android/webkit/WebHistoryItem");
+ LOG_ASSERT(clazz, "Unable to find class android/webkit/WebHistoryItem");
+ gWebHistoryItem.mInit = env->GetMethodID(clazz, "<init>", "()V");
+ LOG_ASSERT(gWebHistoryItem.mInit, "Could not find WebHistoryItem constructor");
+ gWebHistoryItem.mUpdate = env->GetMethodID(clazz, "update",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/graphics/Bitmap;[B)V");
+ LOG_ASSERT(gWebHistoryItem.mUpdate, "Could not find method update in WebHistoryItem");
+
+ // Find the field ids for mTitle and mUrl.
+ gWebHistoryItem.mTitle = env->GetFieldID(clazz, "mTitle", "Ljava/lang/String;");
+ LOG_ASSERT(gWebHistoryItem.mTitle, "Could not find field mTitle in WebHistoryItem");
+ gWebHistoryItem.mUrl = env->GetFieldID(clazz, "mUrl", "Ljava/lang/String;");
+ LOG_ASSERT(gWebHistoryItem.mUrl, "Could not find field mUrl in WebHistoryItem");
+ env->DeleteLocalRef(clazz);
+
+ // Find the WebBackForwardList object and method.
+ clazz = env->FindClass("android/webkit/WebBackForwardList");
+ LOG_ASSERT(clazz, "Unable to find class android/webkit/WebBackForwardList");
+ gWebBackForwardList.mAddHistoryItem = env->GetMethodID(clazz, "addHistoryItem",
+ "(Landroid/webkit/WebHistoryItem;)V");
+ LOG_ASSERT(gWebBackForwardList.mAddHistoryItem, "Could not find method addHistoryItem");
+ gWebBackForwardList.mRemoveHistoryItem = env->GetMethodID(clazz, "removeHistoryItem",
+ "(I)V");
+ LOG_ASSERT(gWebBackForwardList.mRemoveHistoryItem, "Could not find method removeHistoryItem");
+ gWebBackForwardList.mSetCurrentIndex = env->GetMethodID(clazz, "setCurrentIndex", "(I)V");
+ LOG_ASSERT(gWebBackForwardList.mSetCurrentIndex, "Could not find method setCurrentIndex");
+ env->DeleteLocalRef(clazz);
+
+ int result = jniRegisterNativeMethods(env, "android/webkit/WebBackForwardList",
+ gWebBackForwardListMethods, NELEM(gWebBackForwardListMethods));
+ return (result < 0) ? result : jniRegisterNativeMethods(env, "android/webkit/WebHistoryItem",
+ gWebHistoryItemMethods, NELEM(gWebHistoryItemMethods));
+}
+
+} /* namespace android */
diff --git a/Source/WebKit/android/jni/WebHistory.h b/Source/WebKit/android/jni/WebHistory.h
new file mode 100644
index 0000000..2d86aa4
--- /dev/null
+++ b/Source/WebKit/android/jni/WebHistory.h
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_WEBKIT_WEBHISTORY_H
+#define ANDROID_WEBKIT_WEBHISTORY_H
+
+#include "AndroidWebHistoryBridge.h"
+
+#include <jni.h>
+#include <wtf/RefCounted.h>
+#include <wtf/Vector.h>
+
+namespace android {
+
+class AutoJObject;
+
+class WebHistory {
+public:
+ static jbyteArray Flatten(JNIEnv*, WTF::Vector<char>&, WebCore::HistoryItem*);
+ static void AddItem(const AutoJObject&, WebCore::HistoryItem*);
+ static void RemoveItem(const AutoJObject&, int);
+ static void UpdateHistoryIndex(const AutoJObject&, int);
+};
+
+// there are two scale factors saved with each history item. m_scale reflects the
+// viewport scale factor, default to 100 means 100%. m_textWrapScale records
+// the scale factor for wrapping the text paragraph.
+class WebHistoryItem : public WebCore::AndroidWebHistoryBridge {
+public:
+ WebHistoryItem(WebHistoryItem* parent)
+ : WebCore::AndroidWebHistoryBridge(0)
+ , m_parent(parent)
+ , m_object(NULL) { }
+ WebHistoryItem(JNIEnv*, jobject, WebCore::HistoryItem*);
+ ~WebHistoryItem();
+ void updateHistoryItem(WebCore::HistoryItem* item);
+ void setParent(WebHistoryItem* parent) { m_parent = parent; }
+ WebHistoryItem* parent() const { return m_parent.get(); }
+private:
+ RefPtr<WebHistoryItem> m_parent;
+ jweak m_object;
+};
+
+};
+
+#endif
diff --git a/Source/WebKit/android/jni/WebIconDatabase.cpp b/Source/WebKit/android/jni/WebIconDatabase.cpp
new file mode 100644
index 0000000..2a660d1
--- /dev/null
+++ b/Source/WebKit/android/jni/WebIconDatabase.cpp
@@ -0,0 +1,237 @@
+/*
+ * 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 "favicons"
+
+#include "config.h"
+#include "WebIconDatabase.h"
+
+#include "FileSystem.h"
+#include "GraphicsJNI.h"
+#include "IconDatabase.h"
+#include "Image.h"
+#include "IntRect.h"
+#include "JavaSharedClient.h"
+#include "KURL.h"
+#include "WebCoreJni.h"
+
+#include <JNIHelp.h>
+#include <JNIUtility.h>
+#include <SharedBuffer.h>
+#include <SkBitmap.h>
+#include <SkImageDecoder.h>
+#include <SkTemplates.h>
+#include <pthread.h>
+#include <utils/misc.h>
+#include <wtf/Platform.h>
+#include <wtf/text/CString.h>
+
+namespace android {
+
+jobject webcoreImageToJavaBitmap(JNIEnv* env, WebCore::Image* icon)
+{
+ if (!icon)
+ return NULL;
+ SkBitmap bm;
+ WebCore::SharedBuffer* buffer = icon->data();
+ if (!buffer || !SkImageDecoder::DecodeMemory(buffer->data(), buffer->size(),
+ &bm, SkBitmap::kNo_Config,
+ SkImageDecoder::kDecodePixels_Mode))
+ return NULL;
+
+ return GraphicsJNI::createBitmap(env, new SkBitmap(bm), false, NULL);
+}
+
+static WebIconDatabase* gIconDatabaseClient = new WebIconDatabase();
+
+// XXX: Called by the IconDatabase thread
+void WebIconDatabase::dispatchDidAddIconForPageURL(const WTF::String& pageURL)
+{
+ mNotificationsMutex.lock();
+ mNotifications.append(pageURL);
+ if (!mDeliveryRequested) {
+ mDeliveryRequested = true;
+ JavaSharedClient::EnqueueFunctionPtr(DeliverNotifications, this);
+ }
+ mNotificationsMutex.unlock();
+}
+
+// Called in the WebCore thread
+void WebIconDatabase::RegisterForIconNotification(WebIconDatabaseClient* client)
+{
+ WebIconDatabase* db = gIconDatabaseClient;
+ for (unsigned i = 0; i < db->mClients.size(); ++i) {
+ // Do not add the same client twice.
+ if (db->mClients[i] == client)
+ return;
+ }
+ gIconDatabaseClient->mClients.append(client);
+}
+
+// Called in the WebCore thread
+void WebIconDatabase::UnregisterForIconNotification(WebIconDatabaseClient* client)
+{
+ WebIconDatabase* db = gIconDatabaseClient;
+ for (unsigned i = 0; i < db->mClients.size(); ++i) {
+ if (db->mClients[i] == client) {
+ db->mClients.remove(i);
+ break;
+ }
+ }
+}
+
+// Called in the WebCore thread
+void WebIconDatabase::DeliverNotifications(void* v)
+{
+ ASSERT(v);
+ ((WebIconDatabase*)v)->deliverNotifications();
+}
+
+// Called in the WebCore thread
+void WebIconDatabase::deliverNotifications()
+{
+ ASSERT(mDeliveryRequested);
+
+ // Swap the notifications queue
+ Vector<WTF::String> queue;
+ mNotificationsMutex.lock();
+ queue.swap(mNotifications);
+ mDeliveryRequested = false;
+ mNotificationsMutex.unlock();
+
+ // Swap the clients queue
+ Vector<WebIconDatabaseClient*> clients;
+ clients.swap(mClients);
+
+ for (unsigned i = 0; i < queue.size(); ++i) {
+ for (unsigned j = 0; j < clients.size(); ++j) {
+ clients[j]->didAddIconForPageUrl(queue[i]);
+ }
+ }
+}
+
+static void Open(JNIEnv* env, jobject obj, jstring path)
+{
+ WebCore::IconDatabase* iconDb = WebCore::iconDatabase();
+ if (iconDb->isOpen())
+ return;
+ iconDb->setEnabled(true);
+ iconDb->setClient(gIconDatabaseClient);
+ LOG_ASSERT(path, "No path given to nativeOpen");
+ WTF::String pathStr = jstringToWtfString(env, path);
+ WTF::CString fullPath = WebCore::pathByAppendingComponent(pathStr,
+ WebCore::IconDatabase::defaultDatabaseFilename()).utf8();
+ mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
+ bool didSetPermissions = false;
+ if (access(fullPath.data(), F_OK) == 0) {
+ if (chmod(fullPath.data(), mode) == 0)
+ didSetPermissions = true;
+ } else {
+ int fd = open(fullPath.data(), O_CREAT, mode);
+ if (fd >= 0) {
+ close(fd);
+ didSetPermissions = true;
+ }
+ }
+ if (didSetPermissions) {
+ LOGV("Opening WebIconDatabase file '%s'", pathStr.latin1().data());
+ bool res = iconDb->open(pathStr);
+ if (!res)
+ LOGE("Open failed!");
+ } else
+ LOGE("Failed to set permissions on '%s'", fullPath.data());
+}
+
+static void Close(JNIEnv* env, jobject obj)
+{
+ WebCore::iconDatabase()->close();
+}
+
+static void RemoveAllIcons(JNIEnv* env, jobject obj)
+{
+ LOGV("Removing all icons");
+ WebCore::iconDatabase()->removeAllIcons();
+}
+
+static jobject IconForPageUrl(JNIEnv* env, jobject obj, jstring url)
+{
+ LOG_ASSERT(url, "No url given to iconForPageUrl");
+ WTF::String urlStr = jstringToWtfString(env, url);
+
+ WebCore::Image* icon = WebCore::iconDatabase()->iconForPageURL(urlStr,
+ WebCore::IntSize(16, 16));
+ LOGV("Retrieving icon for '%s' %p", urlStr.latin1().data(), icon);
+ return webcoreImageToJavaBitmap(env, icon);
+}
+
+static void RetainIconForPageUrl(JNIEnv* env, jobject obj, jstring url)
+{
+ LOG_ASSERT(url, "No url given to retainIconForPageUrl");
+ WTF::String urlStr = jstringToWtfString(env, url);
+
+ LOGV("Retaining icon for '%s'", urlStr.latin1().data());
+ WebCore::iconDatabase()->retainIconForPageURL(urlStr);
+}
+
+static void ReleaseIconForPageUrl(JNIEnv* env, jobject obj, jstring url)
+{
+ LOG_ASSERT(url, "No url given to releaseIconForPageUrl");
+ WTF::String urlStr = jstringToWtfString(env, url);
+
+ LOGV("Releasing icon for '%s'", urlStr.latin1().data());
+ WebCore::iconDatabase()->releaseIconForPageURL(urlStr);
+}
+
+/*
+ * JNI registration
+ */
+static JNINativeMethod gWebIconDatabaseMethods[] = {
+ { "nativeOpen", "(Ljava/lang/String;)V",
+ (void*) Open },
+ { "nativeClose", "()V",
+ (void*) Close },
+ { "nativeRemoveAllIcons", "()V",
+ (void*) RemoveAllIcons },
+ { "nativeIconForPageUrl", "(Ljava/lang/String;)Landroid/graphics/Bitmap;",
+ (void*) IconForPageUrl },
+ { "nativeRetainIconForPageUrl", "(Ljava/lang/String;)V",
+ (void*) RetainIconForPageUrl },
+ { "nativeReleaseIconForPageUrl", "(Ljava/lang/String;)V",
+ (void*) ReleaseIconForPageUrl }
+};
+
+int registerWebIconDatabase(JNIEnv* env)
+{
+#ifndef NDEBUG
+ jclass webIconDatabase = env->FindClass("android/webkit/WebIconDatabase");
+ LOG_ASSERT(webIconDatabase, "Unable to find class android.webkit.WebIconDatabase");
+ env->DeleteLocalRef(webIconDatabase);
+#endif
+
+ return jniRegisterNativeMethods(env, "android/webkit/WebIconDatabase",
+ gWebIconDatabaseMethods, NELEM(gWebIconDatabaseMethods));
+}
+
+}
diff --git a/Source/WebKit/android/jni/WebIconDatabase.h b/Source/WebKit/android/jni/WebIconDatabase.h
new file mode 100644
index 0000000..b2169aa
--- /dev/null
+++ b/Source/WebKit/android/jni/WebIconDatabase.h
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_WEBKIT_WEBICONDATABASE_H
+#define ANDROID_WEBKIT_WEBICONDATABASE_H
+
+#include "IconDatabaseClient.h"
+#include "PlatformString.h"
+#include "utils/threads.h"
+#include <jni.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+ class Image;
+}
+
+namespace android {
+
+ class WebIconDatabaseClient {
+ public:
+ virtual ~WebIconDatabaseClient() {}
+ virtual void didAddIconForPageUrl(const WTF::String& pageUrl) = 0;
+ };
+
+ class WebIconDatabase : public WebCore::IconDatabaseClient {
+ public:
+ WebIconDatabase() : mDeliveryRequested(false) {}
+ // IconDatabaseClient method
+ virtual void dispatchDidAddIconForPageURL(const WTF::String& pageURL);
+
+ static void RegisterForIconNotification(WebIconDatabaseClient* client);
+ static void UnregisterForIconNotification(WebIconDatabaseClient* client);
+ static void DeliverNotifications(void*);
+
+ private:
+ // Deliver all the icon notifications
+ void deliverNotifications();
+
+ // List of clients.
+ Vector<WebIconDatabaseClient*> mClients;
+
+ // Queue of page urls that have received an icon.
+ Vector<WTF::String> mNotifications;
+ android::Mutex mNotificationsMutex;
+ // Flag to indicate that we have requested a delivery of notifications.
+ bool mDeliveryRequested;
+ };
+
+ jobject webcoreImageToJavaBitmap(JNIEnv* env, WebCore::Image* icon);
+
+};
+
+#endif
diff --git a/Source/WebKit/android/jni/WebSettings.cpp b/Source/WebKit/android/jni/WebSettings.cpp
new file mode 100644
index 0000000..468e7b0
--- /dev/null
+++ b/Source/WebKit/android/jni/WebSettings.cpp
@@ -0,0 +1,599 @@
+/*
+ * Copyright 2007, 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 "websettings"
+
+#include <config.h>
+#include <wtf/Platform.h>
+
+#include "ApplicationCacheStorage.h"
+#include "BitmapAllocatorAndroid.h"
+#include "CachedResourceLoader.h"
+#include "ChromiumIncludes.h"
+#include "DatabaseTracker.h"
+#include "Database.h"
+#include "Document.h"
+#include "EditorClientAndroid.h"
+#include "FileSystem.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameView.h"
+#include "GeolocationPermissions.h"
+#include "GeolocationPositionCache.h"
+#include "Page.h"
+#include "PageCache.h"
+#include "RenderTable.h"
+#include "SQLiteFileSystem.h"
+#include "Settings.h"
+#include "WebCoreFrameBridge.h"
+#include "WebCoreJni.h"
+#if USE(V8)
+#include "WorkerContextExecutionProxy.h"
+#endif
+#include "WebRequestContext.h"
+#include "WebViewCore.h"
+
+#include <JNIHelp.h>
+#include <utils/misc.h>
+#include <wtf/text/CString.h>
+
+namespace android {
+
+static const int permissionFlags660 = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
+
+struct FieldIds {
+ FieldIds(JNIEnv* env, jclass clazz) {
+ mLayoutAlgorithm = env->GetFieldID(clazz, "mLayoutAlgorithm",
+ "Landroid/webkit/WebSettings$LayoutAlgorithm;");
+ mTextSize = env->GetFieldID(clazz, "mTextSize",
+ "Landroid/webkit/WebSettings$TextSize;");
+ mStandardFontFamily = env->GetFieldID(clazz, "mStandardFontFamily",
+ "Ljava/lang/String;");
+ mFixedFontFamily = env->GetFieldID(clazz, "mFixedFontFamily",
+ "Ljava/lang/String;");
+ mSansSerifFontFamily = env->GetFieldID(clazz, "mSansSerifFontFamily",
+ "Ljava/lang/String;");
+ mSerifFontFamily = env->GetFieldID(clazz, "mSerifFontFamily",
+ "Ljava/lang/String;");
+ mCursiveFontFamily = env->GetFieldID(clazz, "mCursiveFontFamily",
+ "Ljava/lang/String;");
+ mFantasyFontFamily = env->GetFieldID(clazz, "mFantasyFontFamily",
+ "Ljava/lang/String;");
+ mDefaultTextEncoding = env->GetFieldID(clazz, "mDefaultTextEncoding",
+ "Ljava/lang/String;");
+ mUserAgent = env->GetFieldID(clazz, "mUserAgent",
+ "Ljava/lang/String;");
+ mAcceptLanguage = env->GetFieldID(clazz, "mAcceptLanguage", "Ljava/lang/String;");
+ mMinimumFontSize = env->GetFieldID(clazz, "mMinimumFontSize", "I");
+ mMinimumLogicalFontSize = env->GetFieldID(clazz, "mMinimumLogicalFontSize", "I");
+ mDefaultFontSize = env->GetFieldID(clazz, "mDefaultFontSize", "I");
+ mDefaultFixedFontSize = env->GetFieldID(clazz, "mDefaultFixedFontSize", "I");
+ mLoadsImagesAutomatically = env->GetFieldID(clazz, "mLoadsImagesAutomatically", "Z");
+#ifdef ANDROID_BLOCK_NETWORK_IMAGE
+ mBlockNetworkImage = env->GetFieldID(clazz, "mBlockNetworkImage", "Z");
+#endif
+ mBlockNetworkLoads = env->GetFieldID(clazz, "mBlockNetworkLoads", "Z");
+ mJavaScriptEnabled = env->GetFieldID(clazz, "mJavaScriptEnabled", "Z");
+ mPluginState = env->GetFieldID(clazz, "mPluginState",
+ "Landroid/webkit/WebSettings$PluginState;");
+#if ENABLE(DATABASE)
+ mDatabaseEnabled = env->GetFieldID(clazz, "mDatabaseEnabled", "Z");
+#endif
+#if ENABLE(DOM_STORAGE)
+ mDomStorageEnabled = env->GetFieldID(clazz, "mDomStorageEnabled", "Z");
+#endif
+#if ENABLE(DATABASE) || ENABLE(DOM_STORAGE)
+ // The databases saved to disk for both the SQL and DOM Storage APIs are stored
+ // in the same base directory.
+ mDatabasePath = env->GetFieldID(clazz, "mDatabasePath", "Ljava/lang/String;");
+ mDatabasePathHasBeenSet = env->GetFieldID(clazz, "mDatabasePathHasBeenSet", "Z");
+#endif
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ mAppCacheEnabled = env->GetFieldID(clazz, "mAppCacheEnabled", "Z");
+ mAppCachePath = env->GetFieldID(clazz, "mAppCachePath", "Ljava/lang/String;");
+ mAppCacheMaxSize = env->GetFieldID(clazz, "mAppCacheMaxSize", "J");
+#endif
+#if ENABLE(WORKERS)
+ mWorkersEnabled = env->GetFieldID(clazz, "mWorkersEnabled", "Z");
+#endif
+ mGeolocationEnabled = env->GetFieldID(clazz, "mGeolocationEnabled", "Z");
+ mGeolocationDatabasePath = env->GetFieldID(clazz, "mGeolocationDatabasePath", "Ljava/lang/String;");
+ mXSSAuditorEnabled = env->GetFieldID(clazz, "mXSSAuditorEnabled", "Z");
+ mJavaScriptCanOpenWindowsAutomatically = env->GetFieldID(clazz,
+ "mJavaScriptCanOpenWindowsAutomatically", "Z");
+ mUseWideViewport = env->GetFieldID(clazz, "mUseWideViewport", "Z");
+ mSupportMultipleWindows = env->GetFieldID(clazz, "mSupportMultipleWindows", "Z");
+ mShrinksStandaloneImagesToFit = env->GetFieldID(clazz, "mShrinksStandaloneImagesToFit", "Z");
+ mMaximumDecodedImageSize = env->GetFieldID(clazz, "mMaximumDecodedImageSize", "J");
+ mPrivateBrowsingEnabled = env->GetFieldID(clazz, "mPrivateBrowsingEnabled", "Z");
+ mSyntheticLinksEnabled = env->GetFieldID(clazz, "mSyntheticLinksEnabled", "Z");
+ mUseDoubleTree = env->GetFieldID(clazz, "mUseDoubleTree", "Z");
+ mPageCacheCapacity = env->GetFieldID(clazz, "mPageCacheCapacity", "I");
+#if ENABLE(WEB_AUTOFILL)
+ mAutoFillEnabled = env->GetFieldID(clazz, "mAutoFillEnabled", "Z");
+ mAutoFillProfile = env->GetFieldID(clazz, "mAutoFillProfile", "Landroid/webkit/WebSettings$AutoFillProfile;");
+ jclass autoFillProfileClass = env->FindClass("android/webkit/WebSettings$AutoFillProfile");
+ mAutoFillProfileFullName = env->GetFieldID(autoFillProfileClass, "mFullName", "Ljava/lang/String;");
+ mAutoFillProfileEmailAddress = env->GetFieldID(autoFillProfileClass, "mEmailAddress", "Ljava/lang/String;");
+ mAutoFillProfileCompanyName = env->GetFieldID(autoFillProfileClass, "mCompanyName", "Ljava/lang/String;");
+ mAutoFillProfileAddressLine1 = env->GetFieldID(autoFillProfileClass, "mAddressLine1", "Ljava/lang/String;");
+ mAutoFillProfileAddressLine2 = env->GetFieldID(autoFillProfileClass, "mAddressLine2", "Ljava/lang/String;");
+ mAutoFillProfileCity = env->GetFieldID(autoFillProfileClass, "mCity", "Ljava/lang/String;");
+ mAutoFillProfileState = env->GetFieldID(autoFillProfileClass, "mState", "Ljava/lang/String;");
+ mAutoFillProfileZipCode = env->GetFieldID(autoFillProfileClass, "mZipCode", "Ljava/lang/String;");
+ mAutoFillProfileCountry = env->GetFieldID(autoFillProfileClass, "mCountry", "Ljava/lang/String;");
+ mAutoFillProfilePhoneNumber = env->GetFieldID(autoFillProfileClass, "mPhoneNumber", "Ljava/lang/String;");
+ env->DeleteLocalRef(autoFillProfileClass);
+#endif
+#if USE(CHROME_NETWORK_STACK)
+ mOverrideCacheMode = env->GetFieldID(clazz, "mOverrideCacheMode", "I");
+#endif
+
+ LOG_ASSERT(mLayoutAlgorithm, "Could not find field mLayoutAlgorithm");
+ LOG_ASSERT(mTextSize, "Could not find field mTextSize");
+ LOG_ASSERT(mStandardFontFamily, "Could not find field mStandardFontFamily");
+ LOG_ASSERT(mFixedFontFamily, "Could not find field mFixedFontFamily");
+ LOG_ASSERT(mSansSerifFontFamily, "Could not find field mSansSerifFontFamily");
+ LOG_ASSERT(mSerifFontFamily, "Could not find field mSerifFontFamily");
+ LOG_ASSERT(mCursiveFontFamily, "Could not find field mCursiveFontFamily");
+ LOG_ASSERT(mFantasyFontFamily, "Could not find field mFantasyFontFamily");
+ LOG_ASSERT(mDefaultTextEncoding, "Could not find field mDefaultTextEncoding");
+ LOG_ASSERT(mUserAgent, "Could not find field mUserAgent");
+ LOG_ASSERT(mAcceptLanguage, "Could not find field mAcceptLanguage");
+ LOG_ASSERT(mMinimumFontSize, "Could not find field mMinimumFontSize");
+ LOG_ASSERT(mMinimumLogicalFontSize, "Could not find field mMinimumLogicalFontSize");
+ LOG_ASSERT(mDefaultFontSize, "Could not find field mDefaultFontSize");
+ LOG_ASSERT(mDefaultFixedFontSize, "Could not find field mDefaultFixedFontSize");
+ LOG_ASSERT(mLoadsImagesAutomatically, "Could not find field mLoadsImagesAutomatically");
+#ifdef ANDROID_BLOCK_NETWORK_IMAGE
+ LOG_ASSERT(mBlockNetworkImage, "Could not find field mBlockNetworkImage");
+#endif
+ LOG_ASSERT(mBlockNetworkLoads, "Could not find field mBlockNetworkLoads");
+ LOG_ASSERT(mJavaScriptEnabled, "Could not find field mJavaScriptEnabled");
+ LOG_ASSERT(mPluginState, "Could not find field mPluginState");
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ LOG_ASSERT(mAppCacheEnabled, "Could not find field mAppCacheEnabled");
+ LOG_ASSERT(mAppCachePath, "Could not find field mAppCachePath");
+ LOG_ASSERT(mAppCacheMaxSize, "Could not find field mAppCacheMaxSize");
+#endif
+#if ENABLE(WORKERS)
+ LOG_ASSERT(mWorkersEnabled, "Could not find field mWorkersEnabled");
+#endif
+ LOG_ASSERT(mJavaScriptCanOpenWindowsAutomatically,
+ "Could not find field mJavaScriptCanOpenWindowsAutomatically");
+ LOG_ASSERT(mUseWideViewport, "Could not find field mUseWideViewport");
+ LOG_ASSERT(mSupportMultipleWindows, "Could not find field mSupportMultipleWindows");
+ LOG_ASSERT(mShrinksStandaloneImagesToFit, "Could not find field mShrinksStandaloneImagesToFit");
+ LOG_ASSERT(mMaximumDecodedImageSize, "Could not find field mMaximumDecodedImageSize");
+ LOG_ASSERT(mUseDoubleTree, "Could not find field mUseDoubleTree");
+ LOG_ASSERT(mPageCacheCapacity, "Could not find field mPageCacheCapacity");
+
+ jclass enumClass = env->FindClass("java/lang/Enum");
+ LOG_ASSERT(enumClass, "Could not find Enum class!");
+ mOrdinal = env->GetMethodID(enumClass, "ordinal", "()I");
+ LOG_ASSERT(mOrdinal, "Could not find method ordinal");
+ env->DeleteLocalRef(enumClass);
+
+ jclass textSizeClass = env->FindClass("android/webkit/WebSettings$TextSize");
+ LOG_ASSERT(textSizeClass, "Could not find TextSize enum");
+ mTextSizeValue = env->GetFieldID(textSizeClass, "value", "I");
+ env->DeleteLocalRef(textSizeClass);
+ }
+
+ // Field ids
+ jfieldID mLayoutAlgorithm;
+ jfieldID mTextSize;
+ jfieldID mStandardFontFamily;
+ jfieldID mFixedFontFamily;
+ jfieldID mSansSerifFontFamily;
+ jfieldID mSerifFontFamily;
+ jfieldID mCursiveFontFamily;
+ jfieldID mFantasyFontFamily;
+ jfieldID mDefaultTextEncoding;
+ jfieldID mUserAgent;
+ jfieldID mAcceptLanguage;
+ jfieldID mMinimumFontSize;
+ jfieldID mMinimumLogicalFontSize;
+ jfieldID mDefaultFontSize;
+ jfieldID mDefaultFixedFontSize;
+ jfieldID mLoadsImagesAutomatically;
+#ifdef ANDROID_BLOCK_NETWORK_IMAGE
+ jfieldID mBlockNetworkImage;
+#endif
+ jfieldID mBlockNetworkLoads;
+ jfieldID mJavaScriptEnabled;
+ jfieldID mPluginState;
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ jfieldID mAppCacheEnabled;
+ jfieldID mAppCachePath;
+ jfieldID mAppCacheMaxSize;
+#endif
+#if ENABLE(WORKERS)
+ jfieldID mWorkersEnabled;
+#endif
+ jfieldID mJavaScriptCanOpenWindowsAutomatically;
+ jfieldID mUseWideViewport;
+ jfieldID mSupportMultipleWindows;
+ jfieldID mShrinksStandaloneImagesToFit;
+ jfieldID mMaximumDecodedImageSize;
+ jfieldID mPrivateBrowsingEnabled;
+ jfieldID mSyntheticLinksEnabled;
+ jfieldID mUseDoubleTree;
+ jfieldID mPageCacheCapacity;
+ // Ordinal() method and value field for enums
+ jmethodID mOrdinal;
+ jfieldID mTextSizeValue;
+
+#if ENABLE(DATABASE)
+ jfieldID mDatabaseEnabled;
+#endif
+#if ENABLE(DOM_STORAGE)
+ jfieldID mDomStorageEnabled;
+#endif
+ jfieldID mGeolocationEnabled;
+ jfieldID mGeolocationDatabasePath;
+ jfieldID mXSSAuditorEnabled;
+#if ENABLE(DATABASE) || ENABLE(DOM_STORAGE)
+ jfieldID mDatabasePath;
+ jfieldID mDatabasePathHasBeenSet;
+#endif
+#if ENABLE(WEB_AUTOFILL)
+ jfieldID mAutoFillEnabled;
+ jfieldID mAutoFillProfile;
+ jfieldID mAutoFillProfileFullName;
+ jfieldID mAutoFillProfileEmailAddress;
+ jfieldID mAutoFillProfileCompanyName;
+ jfieldID mAutoFillProfileAddressLine1;
+ jfieldID mAutoFillProfileAddressLine2;
+ jfieldID mAutoFillProfileCity;
+ jfieldID mAutoFillProfileState;
+ jfieldID mAutoFillProfileZipCode;
+ jfieldID mAutoFillProfileCountry;
+ jfieldID mAutoFillProfilePhoneNumber;
+#endif
+#if USE(CHROME_NETWORK_STACK)
+ jfieldID mOverrideCacheMode;
+#endif
+};
+
+static struct FieldIds* gFieldIds;
+
+// Note: This is moved from the old FrameAndroid.cpp
+static void recursiveCleanupForFullLayout(WebCore::RenderObject* obj)
+{
+ obj->setNeedsLayout(true, false);
+#ifdef ANDROID_LAYOUT
+ if (obj->isTable())
+ (static_cast<WebCore::RenderTable *>(obj))->clearSingleColumn();
+#endif
+ for (WebCore::RenderObject* n = obj->firstChild(); n; n = n->nextSibling())
+ recursiveCleanupForFullLayout(n);
+}
+
+#if ENABLE(WEB_AUTOFILL)
+inline string16 getStringFieldAsString16(JNIEnv* env, jobject autoFillProfile, jfieldID fieldId)
+{
+ jstring str = static_cast<jstring>(env->GetObjectField(autoFillProfile, fieldId));
+ return str ? jstringToString16(env, str) : string16();
+}
+
+void syncAutoFillProfile(JNIEnv* env, jobject autoFillProfile, WebAutoFill* webAutoFill)
+{
+ string16 fullName = getStringFieldAsString16(env, autoFillProfile, gFieldIds->mAutoFillProfileFullName);
+ string16 emailAddress = getStringFieldAsString16(env, autoFillProfile, gFieldIds->mAutoFillProfileEmailAddress);
+ string16 companyName = getStringFieldAsString16(env, autoFillProfile, gFieldIds->mAutoFillProfileCompanyName);
+ string16 addressLine1 = getStringFieldAsString16(env, autoFillProfile, gFieldIds->mAutoFillProfileAddressLine1);
+ string16 addressLine2 = getStringFieldAsString16(env, autoFillProfile, gFieldIds->mAutoFillProfileAddressLine2);
+ string16 city = getStringFieldAsString16(env, autoFillProfile, gFieldIds->mAutoFillProfileCity);
+ string16 state = getStringFieldAsString16(env, autoFillProfile, gFieldIds->mAutoFillProfileState);
+ string16 zipCode = getStringFieldAsString16(env, autoFillProfile, gFieldIds->mAutoFillProfileZipCode);
+ string16 country = getStringFieldAsString16(env, autoFillProfile, gFieldIds->mAutoFillProfileCountry);
+ string16 phoneNumber = getStringFieldAsString16(env, autoFillProfile, gFieldIds->mAutoFillProfilePhoneNumber);
+
+ webAutoFill->setProfile(fullName, emailAddress, companyName, addressLine1, addressLine2, city, state, zipCode, country, phoneNumber);
+}
+#endif
+
+class WebSettings {
+public:
+ static void Sync(JNIEnv* env, jobject obj, jint frame)
+ {
+ WebCore::Frame* pFrame = (WebCore::Frame*)frame;
+ LOG_ASSERT(pFrame, "%s must take a valid frame pointer!", __FUNCTION__);
+ WebCore::Settings* s = pFrame->settings();
+ if (!s)
+ return;
+ WebCore::CachedResourceLoader* cachedResourceLoader = pFrame->document()->cachedResourceLoader();
+
+#ifdef ANDROID_LAYOUT
+ jobject layout = env->GetObjectField(obj, gFieldIds->mLayoutAlgorithm);
+ WebCore::Settings::LayoutAlgorithm l = (WebCore::Settings::LayoutAlgorithm)
+ env->CallIntMethod(layout, gFieldIds->mOrdinal);
+ if (s->layoutAlgorithm() != l) {
+ s->setLayoutAlgorithm(l);
+ if (pFrame->document()) {
+ pFrame->document()->styleSelectorChanged(WebCore::RecalcStyleImmediately);
+ if (pFrame->document()->renderer()) {
+ recursiveCleanupForFullLayout(pFrame->document()->renderer());
+ LOG_ASSERT(pFrame->view(), "No view for this frame when trying to relayout");
+ pFrame->view()->layout();
+ // FIXME: This call used to scroll the page to put the focus into view.
+ // It worked on the WebViewCore, but now scrolling is done outside of the
+ // WebViewCore, on the UI side, so there needs to be a new way to do this.
+ //pFrame->makeFocusVisible();
+ }
+ }
+ }
+#endif
+ jobject textSize = env->GetObjectField(obj, gFieldIds->mTextSize);
+ float zoomFactor = env->GetIntField(textSize, gFieldIds->mTextSizeValue) / 100.0f;
+ if (pFrame->textZoomFactor() != zoomFactor)
+ pFrame->setTextZoomFactor(zoomFactor);
+
+ jstring str = (jstring)env->GetObjectField(obj, gFieldIds->mStandardFontFamily);
+ s->setStandardFontFamily(jstringToWtfString(env, str));
+
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mFixedFontFamily);
+ s->setFixedFontFamily(jstringToWtfString(env, str));
+
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mSansSerifFontFamily);
+ s->setSansSerifFontFamily(jstringToWtfString(env, str));
+
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mSerifFontFamily);
+ s->setSerifFontFamily(jstringToWtfString(env, str));
+
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mCursiveFontFamily);
+ s->setCursiveFontFamily(jstringToWtfString(env, str));
+
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mFantasyFontFamily);
+ s->setFantasyFontFamily(jstringToWtfString(env, str));
+
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mDefaultTextEncoding);
+ s->setDefaultTextEncodingName(jstringToWtfString(env, str));
+
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mUserAgent);
+ WebFrame::getWebFrame(pFrame)->setUserAgent(jstringToWtfString(env, str));
+#if USE(CHROME_NETWORK_STACK)
+ WebViewCore::getWebViewCore(pFrame->view())->setWebRequestContextUserAgent();
+
+ jint cacheMode = env->GetIntField(obj, gFieldIds->mOverrideCacheMode);
+ WebViewCore::getWebViewCore(pFrame->view())->setWebRequestContextCacheMode(cacheMode);
+
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mAcceptLanguage);
+ WebRequestContext::setAcceptLanguage(jstringToWtfString(env, str));
+#endif
+
+ jint size = env->GetIntField(obj, gFieldIds->mMinimumFontSize);
+ s->setMinimumFontSize(size);
+
+ size = env->GetIntField(obj, gFieldIds->mMinimumLogicalFontSize);
+ s->setMinimumLogicalFontSize(size);
+
+ size = env->GetIntField(obj, gFieldIds->mDefaultFontSize);
+ s->setDefaultFontSize(size);
+
+ size = env->GetIntField(obj, gFieldIds->mDefaultFixedFontSize);
+ s->setDefaultFixedFontSize(size);
+
+ jboolean flag = env->GetBooleanField(obj, gFieldIds->mLoadsImagesAutomatically);
+ s->setLoadsImagesAutomatically(flag);
+ if (flag)
+ cachedResourceLoader->setAutoLoadImages(true);
+
+#ifdef ANDROID_BLOCK_NETWORK_IMAGE
+ flag = env->GetBooleanField(obj, gFieldIds->mBlockNetworkImage);
+ s->setBlockNetworkImage(flag);
+ if(!flag)
+ cachedResourceLoader->setBlockNetworkImage(false);
+#endif
+ flag = env->GetBooleanField(obj, gFieldIds->mBlockNetworkLoads);
+ WebFrame* webFrame = WebFrame::getWebFrame(pFrame);
+ webFrame->setBlockNetworkLoads(flag);
+
+ flag = env->GetBooleanField(obj, gFieldIds->mJavaScriptEnabled);
+ s->setJavaScriptEnabled(flag);
+
+ // ON = 0
+ // ON_DEMAND = 1
+ // OFF = 2
+ jobject pluginState = env->GetObjectField(obj, gFieldIds->mPluginState);
+ int state = env->CallIntMethod(pluginState, gFieldIds->mOrdinal);
+ s->setPluginsEnabled(state < 2);
+#ifdef ANDROID_PLUGINS
+ s->setPluginsOnDemand(state == 1);
+#endif
+
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ flag = env->GetBooleanField(obj, gFieldIds->mAppCacheEnabled);
+ s->setOfflineWebApplicationCacheEnabled(flag);
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mAppCachePath);
+ if (str) {
+ String path = jstringToWtfString(env, str);
+ if (path.length() && cacheStorage().cacheDirectory().isNull()) {
+ cacheStorage().setCacheDirectory(path);
+ // This database is created on the first load. If the file
+ // doesn't exist, we create it and set its permissions. The
+ // filename must match that in ApplicationCacheStorage.cpp.
+ String filename = pathByAppendingComponent(path, "ApplicationCache.db");
+ int fd = open(filename.utf8().data(), O_CREAT | O_EXCL, permissionFlags660);
+ if (fd >= 0)
+ close(fd);
+ }
+ }
+ jlong maxsize = env->GetLongField(obj, gFieldIds->mAppCacheMaxSize);
+ cacheStorage().setMaximumSize(maxsize);
+#endif
+
+ flag = env->GetBooleanField(obj, gFieldIds->mJavaScriptCanOpenWindowsAutomatically);
+ s->setJavaScriptCanOpenWindowsAutomatically(flag);
+
+#ifdef ANDROID_LAYOUT
+ flag = env->GetBooleanField(obj, gFieldIds->mUseWideViewport);
+ s->setUseWideViewport(flag);
+#endif
+
+#ifdef ANDROID_MULTIPLE_WINDOWS
+ flag = env->GetBooleanField(obj, gFieldIds->mSupportMultipleWindows);
+ s->setSupportMultipleWindows(flag);
+#endif
+ flag = env->GetBooleanField(obj, gFieldIds->mShrinksStandaloneImagesToFit);
+ s->setShrinksStandaloneImagesToFit(flag);
+ jlong maxImage = env->GetLongField(obj, gFieldIds->mMaximumDecodedImageSize);
+ // Since in ImageSourceAndroid.cpp, the image will always not exceed
+ // MAX_SIZE_BEFORE_SUBSAMPLE, there's no need to pass the max value to
+ // WebCore, which checks (image_width * image_height * 4) as an
+ // estimation against the max value, which is done in CachedImage.cpp.
+ // And there're cases where the decoded image size will not
+ // exceed the max, but the WebCore estimation will. So the following
+ // code is commented out to fix those cases.
+ // if (maxImage == 0)
+ // maxImage = computeMaxBitmapSizeForCache();
+ s->setMaximumDecodedImageSize(maxImage);
+
+ flag = env->GetBooleanField(obj, gFieldIds->mPrivateBrowsingEnabled);
+ s->setPrivateBrowsingEnabled(flag);
+
+ flag = env->GetBooleanField(obj, gFieldIds->mSyntheticLinksEnabled);
+ s->setDefaultFormatDetection(flag);
+ s->setFormatDetectionAddress(flag);
+ s->setFormatDetectionEmail(flag);
+ s->setFormatDetectionTelephone(flag);
+#if ENABLE(DATABASE)
+ flag = env->GetBooleanField(obj, gFieldIds->mDatabaseEnabled);
+ WebCore::Database::setIsAvailable(flag);
+
+ flag = env->GetBooleanField(obj, gFieldIds->mDatabasePathHasBeenSet);
+ if (flag) {
+ // If the user has set the database path, sync it to the DatabaseTracker.
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mDatabasePath);
+ if (str) {
+ String path = jstringToWtfString(env, str);
+ DatabaseTracker::tracker().setDatabaseDirectoryPath(path);
+ // This database is created when the first HTML5 Database object is
+ // instantiated. If the file doesn't exist, we create it and set its
+ // permissions. The filename must match that in
+ // DatabaseTracker.cpp.
+ String filename = SQLiteFileSystem::appendDatabaseFileNameToPath(path, "Databases.db");
+ int fd = open(filename.utf8().data(), O_CREAT | O_EXCL, permissionFlags660);
+ if (fd >= 0)
+ close(fd);
+ }
+ }
+#endif
+#if ENABLE(DOM_STORAGE)
+ flag = env->GetBooleanField(obj, gFieldIds->mDomStorageEnabled);
+ s->setLocalStorageEnabled(flag);
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mDatabasePath);
+ if (str) {
+ WTF::String localStorageDatabasePath = jstringToWtfString(env,str);
+ if (localStorageDatabasePath.length()) {
+ localStorageDatabasePath = WebCore::pathByAppendingComponent(
+ localStorageDatabasePath, "localstorage");
+ // We need 770 for folders
+ mkdir(localStorageDatabasePath.utf8().data(),
+ permissionFlags660 | S_IXUSR | S_IXGRP);
+ s->setLocalStorageDatabasePath(localStorageDatabasePath);
+ }
+ }
+#endif
+
+ flag = env->GetBooleanField(obj, gFieldIds->mGeolocationEnabled);
+ GeolocationPermissions::setAlwaysDeny(!flag);
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mGeolocationDatabasePath);
+ if (str) {
+ String path = jstringToWtfString(env, str);
+ GeolocationPermissions::setDatabasePath(path);
+ GeolocationPositionCache::instance()->setDatabasePath(path);
+ // This database is created when the first Geolocation object is
+ // instantiated. If the file doesn't exist, we create it and set its
+ // permissions. The filename must match that in
+ // GeolocationPositionCache.cpp.
+ String filename = SQLiteFileSystem::appendDatabaseFileNameToPath(path, "CachedGeoposition.db");
+ int fd = open(filename.utf8().data(), O_CREAT | O_EXCL, permissionFlags660);
+ if (fd >= 0)
+ close(fd);
+ }
+
+ flag = env->GetBooleanField(obj, gFieldIds->mXSSAuditorEnabled);
+ s->setXSSAuditorEnabled(flag);
+
+ size = env->GetIntField(obj, gFieldIds->mPageCacheCapacity);
+ if (size > 0) {
+ s->setUsesPageCache(true);
+ WebCore::pageCache()->setCapacity(size);
+ } else
+ s->setUsesPageCache(false);
+
+#if ENABLE(WEB_AUTOFILL)
+ flag = env->GetBooleanField(obj, gFieldIds->mAutoFillEnabled);
+ // TODO: This updates the Settings WebCore side with the user's
+ // preference for autofill and will stop WebCore making requests
+ // into the chromium autofill code. That code in Chromium also has
+ // a notion of being enabled/disabled that gets read from the users
+ // preferences. At the moment, it's hardcoded to true on Android
+ // (see chrome/browser/autofill/autofill_manager.cc:405). This
+ // setting should probably be synced into Chromium also.
+
+ s->setAutoFillEnabled(flag);
+
+ if (flag) {
+ EditorClientAndroid* editorC = static_cast<EditorClientAndroid*>(pFrame->page()->editorClient());
+ WebAutoFill* webAutoFill = editorC->getAutoFill();
+ // Set the active AutoFillProfile data.
+ jobject autoFillProfile = env->GetObjectField(obj, gFieldIds->mAutoFillProfile);
+ if (autoFillProfile)
+ syncAutoFillProfile(env, autoFillProfile, webAutoFill);
+ else {
+ // The autofill profile is null. We need to tell Chromium about this because
+ // this may be because the user just deleted their profile but left the
+ // autofill feature setting enabled.
+ webAutoFill->clearProfiles();
+ }
+ }
+#endif
+ }
+};
+
+
+//-------------------------------------------------------------
+// JNI registration
+//-------------------------------------------------------------
+
+static JNINativeMethod gWebSettingsMethods[] = {
+ { "nativeSync", "(I)V",
+ (void*) WebSettings::Sync }
+};
+
+int registerWebSettings(JNIEnv* env)
+{
+ jclass clazz = env->FindClass("android/webkit/WebSettings");
+ LOG_ASSERT(clazz, "Unable to find class WebSettings!");
+ gFieldIds = new FieldIds(env, clazz);
+ env->DeleteLocalRef(clazz);
+ return jniRegisterNativeMethods(env, "android/webkit/WebSettings",
+ gWebSettingsMethods, NELEM(gWebSettingsMethods));
+}
+
+}
diff --git a/Source/WebKit/android/jni/WebStorage.cpp b/Source/WebKit/android/jni/WebStorage.cpp
new file mode 100644
index 0000000..9ce207d
--- /dev/null
+++ b/Source/WebKit/android/jni/WebStorage.cpp
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2009, 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.
+ */
+
+#include "config.h"
+
+#if ENABLE(DATABASE)
+
+#include "ApplicationCacheStorage.h"
+#include "DatabaseTracker.h"
+#include "JNIUtility.h"
+#include "JavaSharedClient.h"
+#include "KURL.h"
+#include "PageGroup.h"
+#include "SecurityOrigin.h"
+#include "WebCoreJni.h"
+
+#include <JNIHelp.h>
+
+namespace android {
+
+static jobject GetOrigins(JNIEnv* env, jobject obj)
+{
+ Vector<RefPtr<WebCore::SecurityOrigin> > coreOrigins;
+ WebCore::DatabaseTracker::tracker().origins(coreOrigins);
+ Vector<WebCore::KURL> manifestUrls;
+ if (WebCore::cacheStorage().manifestURLs(&manifestUrls)) {
+ int size = manifestUrls.size();
+ for (int i = 0; i < size; ++i) {
+ RefPtr<WebCore::SecurityOrigin> manifestOrigin = WebCore::SecurityOrigin::create(manifestUrls[i]);
+ if (manifestOrigin.get() == 0)
+ continue;
+ coreOrigins.append(manifestOrigin);
+ }
+ }
+
+ jclass setClass = env->FindClass("java/util/HashSet");
+ jmethodID cid = env->GetMethodID(setClass, "<init>", "()V");
+ jmethodID mid = env->GetMethodID(setClass, "add", "(Ljava/lang/Object;)Z");
+ jobject set = env->NewObject(setClass, cid);
+ env->DeleteLocalRef(setClass);
+
+ for (unsigned i = 0; i < coreOrigins.size(); ++i) {
+ WebCore::SecurityOrigin* origin = coreOrigins[i].get();
+ WTF::String url = origin->toString();
+ jstring jUrl = wtfStringToJstring(env, url);
+ env->CallBooleanMethod(set, mid, jUrl);
+ env->DeleteLocalRef(jUrl);
+ }
+
+ return set;
+}
+
+static unsigned long long GetQuotaForOrigin(JNIEnv* env, jobject obj, jstring origin)
+{
+ WTF::String originStr = jstringToWtfString(env, origin);
+ RefPtr<WebCore::SecurityOrigin> securityOrigin = WebCore::SecurityOrigin::createFromString(originStr);
+ unsigned long long quota = WebCore::DatabaseTracker::tracker().quotaForOrigin(securityOrigin.get());
+ return quota;
+}
+
+static unsigned long long GetUsageForOrigin(JNIEnv* env, jobject obj, jstring origin)
+{
+ WTF::String originStr = jstringToWtfString(env, origin);
+ RefPtr<WebCore::SecurityOrigin> securityOrigin = WebCore::SecurityOrigin::createFromString(originStr);
+ unsigned long long usage = WebCore::DatabaseTracker::tracker().usageForOrigin(securityOrigin.get());
+ Vector<WebCore::KURL> manifestUrls;
+ if (!WebCore::cacheStorage().manifestURLs(&manifestUrls))
+ return usage;
+ int size = manifestUrls.size();
+ for (int i = 0; i < size; ++i) {
+ RefPtr<WebCore::SecurityOrigin> manifestOrigin = WebCore::SecurityOrigin::create(manifestUrls[i]);
+ if (manifestOrigin.get() == 0)
+ continue;
+ if (manifestOrigin->isSameSchemeHostPort(securityOrigin.get())) {
+ int64_t cacheSize = 0;
+ WebCore::cacheStorage().cacheGroupSize(manifestUrls[i].string(), &cacheSize);
+ usage += cacheSize;
+ }
+ }
+ return usage;
+}
+
+static void SetQuotaForOrigin(JNIEnv* env, jobject obj, jstring origin, unsigned long long quota)
+{
+ WTF::String originStr = jstringToWtfString(env, origin);
+ RefPtr<WebCore::SecurityOrigin> securityOrigin = WebCore::SecurityOrigin::createFromString(originStr);
+ WebCore::DatabaseTracker::tracker().setQuota(securityOrigin.get(), quota);
+}
+
+static void DeleteOrigin(JNIEnv* env, jobject obj, jstring origin)
+{
+ WTF::String originStr = jstringToWtfString(env, origin);
+ RefPtr<WebCore::SecurityOrigin> securityOrigin = WebCore::SecurityOrigin::createFromString(originStr);
+ WebCore::DatabaseTracker::tracker().deleteOrigin(securityOrigin.get());
+
+ Vector<WebCore::KURL> manifestUrls;
+ if (!WebCore::cacheStorage().manifestURLs(&manifestUrls))
+ return;
+ int size = manifestUrls.size();
+ for (int i = 0; i < size; ++i) {
+ RefPtr<WebCore::SecurityOrigin> manifestOrigin = WebCore::SecurityOrigin::create(manifestUrls[i]);
+ if (manifestOrigin.get() == 0)
+ continue;
+ if (manifestOrigin->isSameSchemeHostPort(securityOrigin.get()))
+ WebCore::cacheStorage().deleteCacheGroup(manifestUrls[i]);
+ }
+}
+
+static void DeleteAllData(JNIEnv* env, jobject obj)
+{
+ WebCore::DatabaseTracker::tracker().deleteAllDatabases();
+
+ Vector<WebCore::KURL> manifestUrls;
+ if (!WebCore::cacheStorage().manifestURLs(&manifestUrls))
+ return;
+ int size = manifestUrls.size();
+ for (int i = 0; i < size; ++i)
+ WebCore::cacheStorage().deleteCacheGroup(manifestUrls[i]);
+
+ // FIXME: this is a workaround for eliminating any DOM Storage data (both
+ // session and local storage) as there is no functionality inside WebKit at the
+ // moment to do it. That functionality is a WIP in https://bugs.webkit.org/show_bug.cgi?id=51878
+ // and when that patch lands and we merge it, we should move towards that approach instead.
+ WebCore::PageGroup::clearDomStorage();
+}
+
+static void SetAppCacheMaximumSize(JNIEnv* env, jobject obj, unsigned long long size)
+{
+ WebCore::cacheStorage().setMaximumSize(size);
+}
+
+/*
+ * JNI registration
+ */
+static JNINativeMethod gWebStorageMethods[] = {
+ { "nativeGetOrigins", "()Ljava/util/Set;",
+ (void*) GetOrigins },
+ { "nativeGetUsageForOrigin", "(Ljava/lang/String;)J",
+ (void*) GetUsageForOrigin },
+ { "nativeGetQuotaForOrigin", "(Ljava/lang/String;)J",
+ (void*) GetQuotaForOrigin },
+ { "nativeSetQuotaForOrigin", "(Ljava/lang/String;J)V",
+ (void*) SetQuotaForOrigin },
+ { "nativeDeleteOrigin", "(Ljava/lang/String;)V",
+ (void*) DeleteOrigin },
+ { "nativeDeleteAllData", "()V",
+ (void*) DeleteAllData },
+ { "nativeSetAppCacheMaximumSize", "(J)V",
+ (void*) SetAppCacheMaximumSize }
+};
+
+int registerWebStorage(JNIEnv* env)
+{
+#ifndef NDEBUG
+ jclass webStorage = env->FindClass("android/webkit/WebStorage");
+ LOG_ASSERT(webStorage, "Unable to find class android.webkit.WebStorage");
+ env->DeleteLocalRef(webStorage);
+#endif
+
+ return jniRegisterNativeMethods(env, "android/webkit/WebStorage",
+ gWebStorageMethods, NELEM(gWebStorageMethods));
+}
+
+}
+
+#endif //ENABLE(DATABASE)
diff --git a/Source/WebKit/android/jni/WebViewCore.cpp b/Source/WebKit/android/jni/WebViewCore.cpp
new file mode 100644
index 0000000..f2680b5
--- /dev/null
+++ b/Source/WebKit/android/jni/WebViewCore.cpp
@@ -0,0 +1,4598 @@
+/*
+ * 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 "WebViewCore.h"
+
+#include "AccessibilityObject.h"
+#include "Attribute.h"
+#include "BaseLayerAndroid.h"
+#include "CachedNode.h"
+#include "CachedRoot.h"
+#include "Chrome.h"
+#include "ChromeClientAndroid.h"
+#include "ChromiumIncludes.h"
+#include "ClientRect.h"
+#include "ClientRectList.h"
+#include "Color.h"
+#include "CSSPropertyNames.h"
+#include "CSSValueKeywords.h"
+#include "DatabaseTracker.h"
+#include "Document.h"
+#include "DOMWindow.h"
+#include "DOMSelection.h"
+#include "Element.h"
+#include "Editor.h"
+#include "EditorClientAndroid.h"
+#include "EventHandler.h"
+#include "EventNames.h"
+#include "ExceptionCode.h"
+#include "FocusController.h"
+#include "Font.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameLoaderClientAndroid.h"
+#include "FrameTree.h"
+#include "FrameView.h"
+#include "Geolocation.h"
+#include "GraphicsContext.h"
+#include "GraphicsJNI.h"
+#include "HTMLAnchorElement.h"
+#include "HTMLAreaElement.h"
+#include "HTMLElement.h"
+#include "HTMLFormControlElement.h"
+#include "HTMLImageElement.h"
+#include "HTMLInputElement.h"
+#include "HTMLLabelElement.h"
+#include "HTMLMapElement.h"
+#include "HTMLNames.h"
+#include "HTMLOptGroupElement.h"
+#include "HTMLOptionElement.h"
+#include "HTMLSelectElement.h"
+#include "HTMLTextAreaElement.h"
+#include "HistoryItem.h"
+#include "HitTestRequest.h"
+#include "HitTestResult.h"
+#include "InlineTextBox.h"
+#include "MemoryUsage.h"
+#include "NamedNodeMap.h"
+#include "Navigator.h"
+#include "Node.h"
+#include "NodeList.h"
+#include "Page.h"
+#include "PageGroup.h"
+#include "PlatformKeyboardEvent.h"
+#include "PlatformString.h"
+#include "PluginWidgetAndroid.h"
+#include "PluginView.h"
+#include "Position.h"
+#include "ProgressTracker.h"
+#include "Range.h"
+#include "RenderBox.h"
+#include "RenderInline.h"
+#include "RenderLayer.h"
+#include "RenderPart.h"
+#include "RenderText.h"
+#include "RenderTextControl.h"
+#include "RenderThemeAndroid.h"
+#include "RenderView.h"
+#include "ResourceRequest.h"
+#include "SchemeRegistry.h"
+#include "SelectionController.h"
+#include "Settings.h"
+#include "SkANP.h"
+#include "SkTemplates.h"
+#include "SkTDArray.h"
+#include "SkTypes.h"
+#include "SkCanvas.h"
+#include "SkPicture.h"
+#include "SkUtils.h"
+#include "Text.h"
+#include "TypingCommand.h"
+#include "WebCoreFrameBridge.h"
+#include "WebFrameView.h"
+#include "WindowsKeyboardCodes.h"
+#include "android_graphics.h"
+#include "autofill/WebAutoFill.h"
+#include "htmlediting.h"
+#include "markup.h"
+
+#include <JNIHelp.h>
+#include <JNIUtility.h>
+#include <ui/KeycodeLabels.h>
+#include <wtf/CurrentTime.h>
+#include <wtf/text/AtomicString.h>
+#include <wtf/text/StringImpl.h>
+
+#if USE(V8)
+#include "ScriptController.h"
+#include "V8Counters.h"
+#include <wtf/text/CString.h>
+#endif
+
+#if DEBUG_NAV_UI
+#include "SkTime.h"
+#endif
+
+#if ENABLE(TOUCH_EVENTS) // Android
+#include "PlatformTouchEvent.h"
+#endif
+
+#ifdef ANDROID_DOM_LOGGING
+#include "AndroidLog.h"
+#include "RenderTreeAsText.h"
+#include <wtf/text/CString.h>
+
+FILE* gDomTreeFile = 0;
+FILE* gRenderTreeFile = 0;
+#endif
+
+#ifdef ANDROID_INSTRUMENT
+#include "TimeCounter.h"
+#endif
+
+#if USE(ACCELERATED_COMPOSITING)
+#include "GraphicsLayerAndroid.h"
+#include "RenderLayerCompositor.h"
+#endif
+
+/* We pass this flag when recording the actual content, so that we don't spend
+ time actually regionizing complex path clips, when all we really want to do
+ is record them.
+ */
+#define PICT_RECORD_FLAGS SkPicture::kUsePathBoundsForClip_RecordingFlag
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+namespace android {
+
+static SkTDArray<WebViewCore*> gInstanceList;
+
+void WebViewCore::addInstance(WebViewCore* inst) {
+ *gInstanceList.append() = inst;
+}
+
+void WebViewCore::removeInstance(WebViewCore* inst) {
+ int index = gInstanceList.find(inst);
+ LOG_ASSERT(index >= 0, "RemoveInstance inst not found");
+ if (index >= 0) {
+ gInstanceList.removeShuffle(index);
+ }
+}
+
+bool WebViewCore::isInstance(WebViewCore* inst) {
+ return gInstanceList.find(inst) >= 0;
+}
+
+jobject WebViewCore::getApplicationContext() {
+
+ // check to see if there is a valid webviewcore object
+ if (gInstanceList.isEmpty())
+ return 0;
+
+ // get the context from the webview
+ jobject context = gInstanceList[0]->getContext();
+
+ if (!context)
+ return 0;
+
+ // get the application context using JNI
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jclass contextClass = env->GetObjectClass(context);
+ jmethodID appContextMethod = env->GetMethodID(contextClass, "getApplicationContext", "()Landroid/content/Context;");
+ env->DeleteLocalRef(contextClass);
+ jobject result = env->CallObjectMethod(context, appContextMethod);
+ checkException(env);
+ return result;
+}
+
+
+struct WebViewCoreStaticMethods {
+ jmethodID m_isSupportedMediaMimeType;
+} gWebViewCoreStaticMethods;
+
+// Check whether a media mimeType is supported in Android media framework.
+bool WebViewCore::isSupportedMediaMimeType(const WTF::String& mimeType) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jMimeType = wtfStringToJstring(env, mimeType);
+ jclass webViewCore = env->FindClass("android/webkit/WebViewCore");
+ bool val = env->CallStaticBooleanMethod(webViewCore,
+ gWebViewCoreStaticMethods.m_isSupportedMediaMimeType, jMimeType);
+ checkException(env);
+ env->DeleteLocalRef(webViewCore);
+ env->DeleteLocalRef(jMimeType);
+
+ return val;
+}
+
+// ----------------------------------------------------------------------------
+
+#define GET_NATIVE_VIEW(env, obj) ((WebViewCore*)env->GetIntField(obj, gWebViewCoreFields.m_nativeClass))
+
+// Field ids for WebViewCore
+struct WebViewCoreFields {
+ jfieldID m_nativeClass;
+ jfieldID m_viewportWidth;
+ jfieldID m_viewportHeight;
+ jfieldID m_viewportInitialScale;
+ jfieldID m_viewportMinimumScale;
+ jfieldID m_viewportMaximumScale;
+ jfieldID m_viewportUserScalable;
+ jfieldID m_viewportDensityDpi;
+ jfieldID m_webView;
+ jfieldID m_drawIsPaused;
+ jfieldID m_lowMemoryUsageMb;
+ jfieldID m_highMemoryUsageMb;
+ jfieldID m_highUsageDeltaMb;
+} gWebViewCoreFields;
+
+// ----------------------------------------------------------------------------
+
+struct WebViewCore::JavaGlue {
+ jweak m_obj;
+ jmethodID m_scrollTo;
+ jmethodID m_contentDraw;
+ jmethodID m_layersDraw;
+ jmethodID m_requestListBox;
+ jmethodID m_openFileChooser;
+ jmethodID m_requestSingleListBox;
+ jmethodID m_jsAlert;
+ jmethodID m_jsConfirm;
+ jmethodID m_jsPrompt;
+ jmethodID m_jsUnload;
+ jmethodID m_jsInterrupt;
+ jmethodID m_didFirstLayout;
+ jmethodID m_updateViewport;
+ jmethodID m_sendNotifyProgressFinished;
+ jmethodID m_sendViewInvalidate;
+ jmethodID m_updateTextfield;
+ jmethodID m_updateTextSelection;
+ jmethodID m_clearTextEntry;
+ jmethodID m_restoreScale;
+ jmethodID m_needTouchEvents;
+ jmethodID m_requestKeyboard;
+ jmethodID m_requestKeyboardWithSelection;
+ jmethodID m_exceededDatabaseQuota;
+ jmethodID m_reachedMaxAppCacheSize;
+ jmethodID m_populateVisitedLinks;
+ jmethodID m_geolocationPermissionsShowPrompt;
+ jmethodID m_geolocationPermissionsHidePrompt;
+ jmethodID m_getDeviceMotionService;
+ jmethodID m_getDeviceOrientationService;
+ jmethodID m_addMessageToConsole;
+ jmethodID m_formDidBlur;
+ jmethodID m_getPluginClass;
+ jmethodID m_showFullScreenPlugin;
+ jmethodID m_hideFullScreenPlugin;
+ jmethodID m_createSurface;
+ jmethodID m_addSurface;
+ jmethodID m_updateSurface;
+ jmethodID m_destroySurface;
+ jmethodID m_getContext;
+ jmethodID m_keepScreenOn;
+ jmethodID m_sendFindAgain;
+ jmethodID m_showRect;
+ jmethodID m_centerFitRect;
+ jmethodID m_setScrollbarModes;
+ jmethodID m_setInstallableWebApp;
+ jmethodID m_enterFullscreenForVideoLayer;
+ jmethodID m_setWebTextViewAutoFillable;
+ jmethodID m_selectAt;
+ AutoJObject object(JNIEnv* env) {
+ return getRealObject(env, m_obj);
+ }
+};
+
+/*
+ * WebViewCore Implementation
+ */
+
+static jmethodID GetJMethod(JNIEnv* env, jclass clazz, const char name[], const char signature[])
+{
+ jmethodID m = env->GetMethodID(clazz, name, signature);
+ LOG_ASSERT(m, "Could not find method %s", name);
+ return m;
+}
+
+Mutex WebViewCore::gFrameCacheMutex;
+Mutex WebViewCore::gButtonMutex;
+Mutex WebViewCore::gCursorBoundsMutex;
+
+WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* mainframe)
+ : m_pluginInvalTimer(this, &WebViewCore::pluginInvalTimerFired)
+ , m_deviceMotionAndOrientationManager(this)
+{
+ m_mainFrame = mainframe;
+
+ m_popupReply = 0;
+ m_moveGeneration = 0;
+ m_lastGeneration = 0;
+ m_touchGeneration = 0;
+ m_blockTextfieldUpdates = false;
+ // just initial values. These should be set by client
+ m_maxXScroll = 320/4;
+ m_maxYScroll = 240/4;
+ m_textGeneration = 0;
+ m_screenWidth = 320;
+ m_textWrapWidth = 320;
+ m_scale = 1;
+#if ENABLE(TOUCH_EVENTS)
+ m_forwardingTouchEvents = false;
+#endif
+ m_isPaused = false;
+ m_screenOnCounter = 0;
+ m_shouldPaintCaret = true;
+
+ LOG_ASSERT(m_mainFrame, "Uh oh, somehow a frameview was made without an initial frame!");
+
+ jclass clazz = env->GetObjectClass(javaWebViewCore);
+ m_javaGlue = new JavaGlue;
+ m_javaGlue->m_obj = env->NewWeakGlobalRef(javaWebViewCore);
+ m_javaGlue->m_scrollTo = GetJMethod(env, clazz, "contentScrollTo", "(IIZZ)V");
+ m_javaGlue->m_contentDraw = GetJMethod(env, clazz, "contentDraw", "()V");
+ m_javaGlue->m_layersDraw = GetJMethod(env, clazz, "layersDraw", "()V");
+ m_javaGlue->m_requestListBox = GetJMethod(env, clazz, "requestListBox", "([Ljava/lang/String;[I[I)V");
+ m_javaGlue->m_openFileChooser = GetJMethod(env, clazz, "openFileChooser", "(Ljava/lang/String;)Ljava/lang/String;");
+ m_javaGlue->m_requestSingleListBox = GetJMethod(env, clazz, "requestListBox", "([Ljava/lang/String;[II)V");
+ m_javaGlue->m_jsAlert = GetJMethod(env, clazz, "jsAlert", "(Ljava/lang/String;Ljava/lang/String;)V");
+ m_javaGlue->m_jsConfirm = GetJMethod(env, clazz, "jsConfirm", "(Ljava/lang/String;Ljava/lang/String;)Z");
+ m_javaGlue->m_jsPrompt = GetJMethod(env, clazz, "jsPrompt", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
+ m_javaGlue->m_jsUnload = GetJMethod(env, clazz, "jsUnload", "(Ljava/lang/String;Ljava/lang/String;)Z");
+ m_javaGlue->m_jsInterrupt = GetJMethod(env, clazz, "jsInterrupt", "()Z");
+ m_javaGlue->m_didFirstLayout = GetJMethod(env, clazz, "didFirstLayout", "(Z)V");
+ m_javaGlue->m_updateViewport = GetJMethod(env, clazz, "updateViewport", "()V");
+ m_javaGlue->m_sendNotifyProgressFinished = GetJMethod(env, clazz, "sendNotifyProgressFinished", "()V");
+ m_javaGlue->m_sendViewInvalidate = GetJMethod(env, clazz, "sendViewInvalidate", "(IIII)V");
+ m_javaGlue->m_updateTextfield = GetJMethod(env, clazz, "updateTextfield", "(IZLjava/lang/String;I)V");
+ m_javaGlue->m_updateTextSelection = GetJMethod(env, clazz, "updateTextSelection", "(IIII)V");
+ m_javaGlue->m_clearTextEntry = GetJMethod(env, clazz, "clearTextEntry", "()V");
+ m_javaGlue->m_restoreScale = GetJMethod(env, clazz, "restoreScale", "(FF)V");
+ m_javaGlue->m_needTouchEvents = GetJMethod(env, clazz, "needTouchEvents", "(Z)V");
+ m_javaGlue->m_requestKeyboard = GetJMethod(env, clazz, "requestKeyboard", "(Z)V");
+ m_javaGlue->m_requestKeyboardWithSelection = GetJMethod(env, clazz, "requestKeyboardWithSelection", "(IIII)V");
+ m_javaGlue->m_exceededDatabaseQuota = GetJMethod(env, clazz, "exceededDatabaseQuota", "(Ljava/lang/String;Ljava/lang/String;JJ)V");
+ m_javaGlue->m_reachedMaxAppCacheSize = GetJMethod(env, clazz, "reachedMaxAppCacheSize", "(J)V");
+ m_javaGlue->m_populateVisitedLinks = GetJMethod(env, clazz, "populateVisitedLinks", "()V");
+ m_javaGlue->m_geolocationPermissionsShowPrompt = GetJMethod(env, clazz, "geolocationPermissionsShowPrompt", "(Ljava/lang/String;)V");
+ m_javaGlue->m_geolocationPermissionsHidePrompt = GetJMethod(env, clazz, "geolocationPermissionsHidePrompt", "()V");
+ m_javaGlue->m_getDeviceMotionService = GetJMethod(env, clazz, "getDeviceMotionService", "()Landroid/webkit/DeviceMotionService;");
+ m_javaGlue->m_getDeviceOrientationService = GetJMethod(env, clazz, "getDeviceOrientationService", "()Landroid/webkit/DeviceOrientationService;");
+ m_javaGlue->m_addMessageToConsole = GetJMethod(env, clazz, "addMessageToConsole", "(Ljava/lang/String;ILjava/lang/String;I)V");
+ m_javaGlue->m_formDidBlur = GetJMethod(env, clazz, "formDidBlur", "(I)V");
+ m_javaGlue->m_getPluginClass = GetJMethod(env, clazz, "getPluginClass", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Class;");
+ m_javaGlue->m_showFullScreenPlugin = GetJMethod(env, clazz, "showFullScreenPlugin", "(Landroid/webkit/ViewManager$ChildView;I)V");
+ m_javaGlue->m_hideFullScreenPlugin = GetJMethod(env, clazz, "hideFullScreenPlugin", "()V");
+ m_javaGlue->m_createSurface = GetJMethod(env, clazz, "createSurface", "(Landroid/view/View;)Landroid/webkit/ViewManager$ChildView;");
+ m_javaGlue->m_addSurface = GetJMethod(env, clazz, "addSurface", "(Landroid/view/View;IIII)Landroid/webkit/ViewManager$ChildView;");
+ m_javaGlue->m_updateSurface = GetJMethod(env, clazz, "updateSurface", "(Landroid/webkit/ViewManager$ChildView;IIII)V");
+ m_javaGlue->m_destroySurface = GetJMethod(env, clazz, "destroySurface", "(Landroid/webkit/ViewManager$ChildView;)V");
+ m_javaGlue->m_getContext = GetJMethod(env, clazz, "getContext", "()Landroid/content/Context;");
+ m_javaGlue->m_keepScreenOn = GetJMethod(env, clazz, "keepScreenOn", "(Z)V");
+ m_javaGlue->m_sendFindAgain = GetJMethod(env, clazz, "sendFindAgain", "()V");
+ m_javaGlue->m_showRect = GetJMethod(env, clazz, "showRect", "(IIIIIIFFFF)V");
+ m_javaGlue->m_centerFitRect = GetJMethod(env, clazz, "centerFitRect", "(IIII)V");
+ m_javaGlue->m_setScrollbarModes = GetJMethod(env, clazz, "setScrollbarModes", "(II)V");
+ m_javaGlue->m_setInstallableWebApp = GetJMethod(env, clazz, "setInstallableWebApp", "()V");
+#if ENABLE(VIDEO)
+ m_javaGlue->m_enterFullscreenForVideoLayer = GetJMethod(env, clazz, "enterFullscreenForVideoLayer", "(ILjava/lang/String;)V");
+#endif
+ m_javaGlue->m_setWebTextViewAutoFillable = GetJMethod(env, clazz, "setWebTextViewAutoFillable", "(ILjava/lang/String;)V");
+ m_javaGlue->m_selectAt = GetJMethod(env, clazz, "selectAt", "(II)V");
+ env->DeleteLocalRef(clazz);
+
+ env->SetIntField(javaWebViewCore, gWebViewCoreFields.m_nativeClass, (jint)this);
+
+ m_scrollOffsetX = m_scrollOffsetY = 0;
+
+ PageGroup::setShouldTrackVisitedLinks(true);
+
+ reset(true);
+
+ MemoryUsage::setLowMemoryUsageMb(env->GetIntField(javaWebViewCore, gWebViewCoreFields.m_lowMemoryUsageMb));
+ MemoryUsage::setHighMemoryUsageMb(env->GetIntField(javaWebViewCore, gWebViewCoreFields.m_highMemoryUsageMb));
+ MemoryUsage::setHighUsageDeltaMb(env->GetIntField(javaWebViewCore, gWebViewCoreFields.m_highUsageDeltaMb));
+
+ WebViewCore::addInstance(this);
+
+#if USE(CHROME_NETWORK_STACK)
+ AndroidNetworkLibraryImpl::InitWithApplicationContext(env, 0);
+#endif
+}
+
+WebViewCore::~WebViewCore()
+{
+ WebViewCore::removeInstance(this);
+
+ // Release the focused view
+ Release(m_popupReply);
+
+ if (m_javaGlue->m_obj) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->DeleteWeakGlobalRef(m_javaGlue->m_obj);
+ m_javaGlue->m_obj = 0;
+ }
+ delete m_javaGlue;
+ delete m_frameCacheKit;
+ delete m_navPictureKit;
+}
+
+WebViewCore* WebViewCore::getWebViewCore(const WebCore::FrameView* view)
+{
+ return getWebViewCore(static_cast<const WebCore::ScrollView*>(view));
+}
+
+WebViewCore* WebViewCore::getWebViewCore(const WebCore::ScrollView* view)
+{
+ if (!view)
+ return 0;
+
+ WebFrameView* webFrameView = static_cast<WebFrameView*>(view->platformWidget());
+ if (!webFrameView)
+ return 0;
+ return webFrameView->webViewCore();
+}
+
+void WebViewCore::reset(bool fromConstructor)
+{
+ DBG_SET_LOG("");
+ if (fromConstructor) {
+ m_frameCacheKit = 0;
+ m_navPictureKit = 0;
+ } else {
+ gFrameCacheMutex.lock();
+ delete m_frameCacheKit;
+ delete m_navPictureKit;
+ m_frameCacheKit = 0;
+ m_navPictureKit = 0;
+ gFrameCacheMutex.unlock();
+ }
+
+ m_lastFocused = 0;
+ m_blurringNodePointer = 0;
+ m_lastFocusedBounds = WebCore::IntRect(0,0,0,0);
+ m_focusBoundsChanged = false;
+ m_lastFocusedSelStart = 0;
+ m_lastFocusedSelEnd = 0;
+ clearContent();
+ m_updatedFrameCache = true;
+ m_frameCacheOutOfDate = true;
+ m_skipContentDraw = false;
+ m_findIsUp = false;
+ m_domtree_version = 0;
+ m_check_domtree_version = true;
+ m_progressDone = false;
+ m_hasCursorBounds = false;
+
+ m_scrollOffsetX = 0;
+ m_scrollOffsetY = 0;
+ m_screenWidth = 0;
+ m_screenHeight = 0;
+ m_groupForVisitedLinks = 0;
+ m_currentNodeDomNavigationAxis = 0;
+}
+
+static bool layoutIfNeededRecursive(WebCore::Frame* f)
+{
+ if (!f)
+ return true;
+
+ WebCore::FrameView* v = f->view();
+ if (!v)
+ return true;
+
+ if (v->needsLayout())
+ v->layout(f->tree()->parent());
+
+ WebCore::Frame* child = f->tree()->firstChild();
+ bool success = true;
+ while (child) {
+ success &= layoutIfNeededRecursive(child);
+ child = child->tree()->nextSibling();
+ }
+
+ return success && !v->needsLayout();
+}
+
+CacheBuilder& WebViewCore::cacheBuilder()
+{
+ return FrameLoaderClientAndroid::get(m_mainFrame)->getCacheBuilder();
+}
+
+WebCore::Node* WebViewCore::currentFocus()
+{
+ return cacheBuilder().currentFocus();
+}
+
+void WebViewCore::recordPicture(SkPicture* picture)
+{
+ // if there is no document yet, just return
+ if (!m_mainFrame->document()) {
+ DBG_NAV_LOG("no document");
+ return;
+ }
+ // Call layout to ensure that the contentWidth and contentHeight are correct
+ if (!layoutIfNeededRecursive(m_mainFrame)) {
+ DBG_NAV_LOG("layout failed");
+ return;
+ }
+ // draw into the picture's recording canvas
+ WebCore::FrameView* view = m_mainFrame->view();
+ DBG_NAV_LOGD("view=(w=%d,h=%d)", view->contentsWidth(),
+ view->contentsHeight());
+ SkAutoPictureRecord arp(picture, view->contentsWidth(),
+ view->contentsHeight(), PICT_RECORD_FLAGS);
+ SkAutoMemoryUsageProbe mup(__FUNCTION__);
+
+ // Copy m_buttons so we can pass it to our graphics context.
+ gButtonMutex.lock();
+ WTF::Vector<Container> buttons(m_buttons);
+ gButtonMutex.unlock();
+
+ WebCore::PlatformGraphicsContext pgc(arp.getRecordingCanvas(), &buttons);
+ WebCore::GraphicsContext gc(&pgc);
+ view->platformWidget()->draw(&gc, WebCore::IntRect(0, 0,
+ view->contentsWidth(), view->contentsHeight()));
+
+ gButtonMutex.lock();
+ updateButtonList(&buttons);
+ gButtonMutex.unlock();
+}
+
+void WebViewCore::recordPictureSet(PictureSet* content)
+{
+ // if there is no document yet, just return
+ if (!m_mainFrame->document()) {
+ DBG_SET_LOG("!m_mainFrame->document()");
+ return;
+ }
+ // If there is a pending style recalculation, just return.
+ if (m_mainFrame->document()->isPendingStyleRecalc()) {
+ LOGW("recordPictureSet: pending style recalc, ignoring.");
+ return;
+ }
+ if (m_addInval.isEmpty()) {
+ DBG_SET_LOG("m_addInval.isEmpty()");
+ return;
+ }
+ // Call layout to ensure that the contentWidth and contentHeight are correct
+ // it's fine for layout to gather invalidates, but defeat sending a message
+ // back to java to call webkitDraw, since we're already in the middle of
+ // doing that
+ m_skipContentDraw = true;
+ bool success = layoutIfNeededRecursive(m_mainFrame);
+ m_skipContentDraw = false;
+
+ // We may be mid-layout and thus cannot draw.
+ if (!success)
+ return;
+
+ { // collect WebViewCoreRecordTimeCounter after layoutIfNeededRecursive
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreRecordTimeCounter);
+#endif
+
+ // if the webkit page dimensions changed, discard the pictureset and redraw.
+ WebCore::FrameView* view = m_mainFrame->view();
+ int width = view->contentsWidth();
+ int height = view->contentsHeight();
+
+ // Use the contents width and height as a starting point.
+ SkIRect contentRect;
+ contentRect.set(0, 0, width, height);
+ SkIRect total(contentRect);
+
+ // Traverse all the frames and add their sizes if they are in the visible
+ // rectangle.
+ for (WebCore::Frame* frame = m_mainFrame->tree()->traverseNext(); frame;
+ frame = frame->tree()->traverseNext()) {
+ // If the frame doesn't have an owner then it is the top frame and the
+ // view size is the frame size.
+ WebCore::RenderPart* owner = frame->ownerRenderer();
+ if (owner && owner->style()->visibility() == VISIBLE) {
+ int x = owner->x();
+ int y = owner->y();
+
+ // Traverse the tree up to the parent to find the absolute position
+ // of this frame.
+ WebCore::Frame* parent = frame->tree()->parent();
+ while (parent) {
+ WebCore::RenderPart* parentOwner = parent->ownerRenderer();
+ if (parentOwner) {
+ x += parentOwner->x();
+ y += parentOwner->y();
+ }
+ parent = parent->tree()->parent();
+ }
+ // Use the owner dimensions so that padding and border are
+ // included.
+ int right = x + owner->width();
+ int bottom = y + owner->height();
+ SkIRect frameRect = {x, y, right, bottom};
+ // Ignore a width or height that is smaller than 1. Some iframes
+ // have small dimensions in order to be hidden. The iframe
+ // expansion code does not expand in that case so we should ignore
+ // them here.
+ if (frameRect.width() > 1 && frameRect.height() > 1
+ && SkIRect::Intersects(total, frameRect))
+ total.join(x, y, right, bottom);
+ }
+ }
+
+ // If the new total is larger than the content, resize the view to include
+ // all the content.
+ if (!contentRect.contains(total)) {
+ // Resize the view to change the overflow clip.
+ view->resize(total.fRight, total.fBottom);
+
+ // We have to force a layout in order for the clip to change.
+ m_mainFrame->contentRenderer()->setNeedsLayoutAndPrefWidthsRecalc();
+ view->forceLayout();
+
+ // Relayout similar to above
+ m_skipContentDraw = true;
+ bool success = layoutIfNeededRecursive(m_mainFrame);
+ m_skipContentDraw = false;
+ if (!success)
+ return;
+
+ // Set the computed content width
+ width = view->contentsWidth();
+ height = view->contentsHeight();
+ }
+
+ if (cacheBuilder().pictureSetDisabled())
+ content->clear();
+
+ content->checkDimensions(width, height, &m_addInval);
+
+ // The inval region may replace existing pictures. The existing pictures
+ // may have already been split into pieces. If reuseSubdivided() returns
+ // true, the split pieces are the last entries in the picture already. They
+ // are marked as invalid, and are rebuilt by rebuildPictureSet().
+
+ // If the new region doesn't match a set of split pieces, add it to the end.
+ if (!content->reuseSubdivided(m_addInval)) {
+ const SkIRect& inval = m_addInval.getBounds();
+ SkPicture* picture = rebuildPicture(inval);
+ DBG_SET_LOGD("{%d,%d,w=%d,h=%d}", inval.fLeft,
+ inval.fTop, inval.width(), inval.height());
+ content->add(m_addInval, picture, 0, false);
+ SkSafeUnref(picture);
+ }
+ // Remove any pictures already in the set that are obscured by the new one,
+ // and check to see if any already split pieces need to be redrawn.
+ if (content->build())
+ rebuildPictureSet(content);
+ } // WebViewCoreRecordTimeCounter
+ WebCore::Node* oldFocusNode = currentFocus();
+ m_frameCacheOutOfDate = true;
+ WebCore::IntRect oldBounds;
+ int oldSelStart = 0;
+ int oldSelEnd = 0;
+ if (oldFocusNode) {
+ oldBounds = oldFocusNode->getRect();
+ RenderObject* renderer = oldFocusNode->renderer();
+ if (renderer && (renderer->isTextArea() || renderer->isTextField())) {
+ WebCore::RenderTextControl* rtc =
+ static_cast<WebCore::RenderTextControl*>(renderer);
+ oldSelStart = rtc->selectionStart();
+ oldSelEnd = rtc->selectionEnd();
+ }
+ } else
+ oldBounds = WebCore::IntRect(0,0,0,0);
+ unsigned latestVersion = 0;
+ if (m_check_domtree_version) {
+ // as domTreeVersion only increment, we can just check the sum to see
+ // whether we need to update the frame cache
+ for (Frame* frame = m_mainFrame; frame; frame = frame->tree()->traverseNext()) {
+ const Document* doc = frame->document();
+ latestVersion += doc->domTreeVersion() + doc->styleVersion();
+ }
+ }
+ DBG_NAV_LOGD("m_lastFocused=%p oldFocusNode=%p"
+ " m_lastFocusedBounds={%d,%d,%d,%d} oldBounds={%d,%d,%d,%d}"
+ " m_lastFocusedSelection={%d,%d} oldSelection={%d,%d}"
+ " m_check_domtree_version=%s latestVersion=%d m_domtree_version=%d",
+ m_lastFocused, oldFocusNode,
+ m_lastFocusedBounds.x(), m_lastFocusedBounds.y(),
+ m_lastFocusedBounds.width(), m_lastFocusedBounds.height(),
+ oldBounds.x(), oldBounds.y(), oldBounds.width(), oldBounds.height(),
+ m_lastFocusedSelStart, m_lastFocusedSelEnd, oldSelStart, oldSelEnd,
+ m_check_domtree_version ? "true" : "false",
+ latestVersion, m_domtree_version);
+ if (m_lastFocused == oldFocusNode && m_lastFocusedBounds == oldBounds
+ && m_lastFocusedSelStart == oldSelStart
+ && m_lastFocusedSelEnd == oldSelEnd
+ && !m_findIsUp
+ && (!m_check_domtree_version || latestVersion == m_domtree_version))
+ {
+ return;
+ }
+ m_focusBoundsChanged |= m_lastFocused == oldFocusNode
+ && m_lastFocusedBounds != oldBounds;
+ m_lastFocused = oldFocusNode;
+ m_lastFocusedBounds = oldBounds;
+ m_lastFocusedSelStart = oldSelStart;
+ m_lastFocusedSelEnd = oldSelEnd;
+ m_domtree_version = latestVersion;
+ DBG_NAV_LOG("call updateFrameCache");
+ updateFrameCache();
+ if (m_findIsUp) {
+ LOG_ASSERT(m_javaGlue->m_obj,
+ "A Java widget was not associated with this view bridge!");
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_sendFindAgain);
+ checkException(env);
+ }
+}
+
+void WebViewCore::updateButtonList(WTF::Vector<Container>* buttons)
+{
+ // All the entries in buttons are either updates of previous entries in
+ // m_buttons or they need to be added to it.
+ Container* end = buttons->end();
+ for (Container* updatedContainer = buttons->begin();
+ updatedContainer != end; updatedContainer++) {
+ bool updated = false;
+ // Search for a previous entry that references the same node as our new
+ // data
+ Container* lastPossibleMatch = m_buttons.end();
+ for (Container* possibleMatch = m_buttons.begin();
+ possibleMatch != lastPossibleMatch; possibleMatch++) {
+ if (updatedContainer->matches(possibleMatch->node())) {
+ // Update our record, and skip to the next one.
+ possibleMatch->setRect(updatedContainer->rect());
+ updated = true;
+ break;
+ }
+ }
+ if (!updated) {
+ // This is a brand new button, so append it to m_buttons
+ m_buttons.append(*updatedContainer);
+ }
+ }
+ size_t i = 0;
+ // count will decrease each time one is removed, so check count each time.
+ while (i < m_buttons.size()) {
+ if (m_buttons[i].canBeRemoved()) {
+ m_buttons[i] = m_buttons.last();
+ m_buttons.removeLast();
+ } else {
+ i++;
+ }
+ }
+}
+
+// note: updateCursorBounds is called directly by the WebView thread
+// This needs to be called each time we call CachedRoot::setCursor() with
+// non-null CachedNode/CachedFrame, since otherwise the WebViewCore's data
+// about the cursor is incorrect. When we call setCursor(0,0), we need
+// to set hasCursorBounds to false.
+void WebViewCore::updateCursorBounds(const CachedRoot* root,
+ const CachedFrame* cachedFrame, const CachedNode* cachedNode)
+{
+ LOG_ASSERT(root, "updateCursorBounds: root cannot be null");
+ LOG_ASSERT(cachedNode, "updateCursorBounds: cachedNode cannot be null");
+ LOG_ASSERT(cachedFrame, "updateCursorBounds: cachedFrame cannot be null");
+ gCursorBoundsMutex.lock();
+ m_hasCursorBounds = !cachedNode->isHidden();
+ // If m_hasCursorBounds is false, we never look at the other
+ // values, so do not bother setting them.
+ if (m_hasCursorBounds) {
+ WebCore::IntRect bounds = cachedNode->bounds(cachedFrame);
+ if (m_cursorBounds != bounds)
+ DBG_NAV_LOGD("new cursor bounds=(%d,%d,w=%d,h=%d)",
+ bounds.x(), bounds.y(), bounds.width(), bounds.height());
+ m_cursorBounds = bounds;
+ m_cursorHitBounds = cachedNode->hitBounds(cachedFrame);
+ m_cursorFrame = cachedFrame->framePointer();
+ root->getSimulatedMousePosition(&m_cursorLocation);
+ m_cursorNode = cachedNode->nodePointer();
+ }
+ gCursorBoundsMutex.unlock();
+}
+
+void WebViewCore::clearContent()
+{
+ DBG_SET_LOG("");
+ m_content.clear();
+ m_addInval.setEmpty();
+ m_rebuildInval.setEmpty();
+}
+
+bool WebViewCore::focusBoundsChanged()
+{
+ bool result = m_focusBoundsChanged;
+ m_focusBoundsChanged = false;
+ return result;
+}
+
+SkPicture* WebViewCore::rebuildPicture(const SkIRect& inval)
+{
+ WebCore::FrameView* view = m_mainFrame->view();
+ int width = view->contentsWidth();
+ int height = view->contentsHeight();
+ SkPicture* picture = new SkPicture();
+ SkAutoPictureRecord arp(picture, width, height, PICT_RECORD_FLAGS);
+ SkAutoMemoryUsageProbe mup(__FUNCTION__);
+ SkCanvas* recordingCanvas = arp.getRecordingCanvas();
+
+ gButtonMutex.lock();
+ WTF::Vector<Container> buttons(m_buttons);
+ gButtonMutex.unlock();
+
+ WebCore::PlatformGraphicsContext pgc(recordingCanvas, &buttons);
+ WebCore::GraphicsContext gc(&pgc);
+ recordingCanvas->translate(-inval.fLeft, -inval.fTop);
+ recordingCanvas->save();
+ view->platformWidget()->draw(&gc, WebCore::IntRect(inval.fLeft,
+ inval.fTop, inval.width(), inval.height()));
+ m_rebuildInval.op(inval, SkRegion::kUnion_Op);
+ DBG_SET_LOGD("m_rebuildInval={%d,%d,r=%d,b=%d}",
+ m_rebuildInval.getBounds().fLeft, m_rebuildInval.getBounds().fTop,
+ m_rebuildInval.getBounds().fRight, m_rebuildInval.getBounds().fBottom);
+
+ gButtonMutex.lock();
+ updateButtonList(&buttons);
+ gButtonMutex.unlock();
+
+ return picture;
+}
+
+void WebViewCore::rebuildPictureSet(PictureSet* pictureSet)
+{
+ WebCore::FrameView* view = m_mainFrame->view();
+ size_t size = pictureSet->size();
+ for (size_t index = 0; index < size; index++) {
+ if (pictureSet->upToDate(index))
+ continue;
+ const SkIRect& inval = pictureSet->bounds(index);
+ DBG_SET_LOGD("pictSet=%p [%d] {%d,%d,w=%d,h=%d}", pictureSet, index,
+ inval.fLeft, inval.fTop, inval.width(), inval.height());
+ pictureSet->setPicture(index, rebuildPicture(inval));
+ }
+ pictureSet->validate(__FUNCTION__);
+}
+
+BaseLayerAndroid* WebViewCore::createBaseLayer()
+{
+ BaseLayerAndroid* base = new BaseLayerAndroid();
+ base->setContent(m_content);
+
+ bool layoutSucceeded = layoutIfNeededRecursive(m_mainFrame);
+ // Layout only fails if called during a layout.
+ LOG_ASSERT(layoutSucceeded, "Can never be called recursively");
+
+#if USE(ACCELERATED_COMPOSITING)
+ // We set the background color
+ if (m_mainFrame && m_mainFrame->document()
+ && m_mainFrame->document()->body()) {
+ Document* document = m_mainFrame->document();
+ RefPtr<RenderStyle> style = document->styleForElementIgnoringPendingStylesheets(document->body());
+ if (style->hasBackground()) {
+ Color color = style->visitedDependentColor(CSSPropertyBackgroundColor);
+ if (color.isValid() && color.alpha() > 0)
+ base->setBackgroundColor(color);
+ }
+ }
+
+ // We update the layers
+ ChromeClientAndroid* chromeC = static_cast<ChromeClientAndroid*>(m_mainFrame->page()->chrome()->client());
+ GraphicsLayerAndroid* root = static_cast<GraphicsLayerAndroid*>(chromeC->layersSync());
+ if (root) {
+ root->notifyClientAnimationStarted();
+ LayerAndroid* copyLayer = new LayerAndroid(*root->contentLayer());
+ base->addChild(copyLayer);
+ copyLayer->unref();
+ }
+#endif
+
+ return base;
+}
+
+BaseLayerAndroid* WebViewCore::recordContent(SkRegion* region, SkIPoint* point)
+{
+ DBG_SET_LOG("start");
+ float progress = (float) m_mainFrame->page()->progress()->estimatedProgress();
+ m_progressDone = progress <= 0.0f || progress >= 1.0f;
+ recordPictureSet(&m_content);
+ if (!m_progressDone && m_content.isEmpty()) {
+ DBG_SET_LOGD("empty (progress=%g)", progress);
+ return 0;
+ }
+ region->set(m_addInval);
+ m_addInval.setEmpty();
+ region->op(m_rebuildInval, SkRegion::kUnion_Op);
+ m_rebuildInval.setEmpty();
+ point->fX = m_content.width();
+ point->fY = m_content.height();
+ DBG_SET_LOGD("region={%d,%d,r=%d,b=%d}", region->getBounds().fLeft,
+ region->getBounds().fTop, region->getBounds().fRight,
+ region->getBounds().fBottom);
+ DBG_SET_LOG("end");
+
+ return createBaseLayer();
+}
+
+void WebViewCore::splitContent(PictureSet* content)
+{
+ bool layoutSucceeded = layoutIfNeededRecursive(m_mainFrame);
+ LOG_ASSERT(layoutSucceeded, "Can never be called recursively");
+ content->split(&m_content);
+ rebuildPictureSet(&m_content);
+ content->set(m_content);
+}
+
+void WebViewCore::scrollTo(int x, int y, bool animate)
+{
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+
+// LOGD("WebViewCore::scrollTo(%d %d)\n", x, y);
+
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_scrollTo,
+ x, y, animate, false);
+ checkException(env);
+}
+
+void WebViewCore::sendNotifyProgressFinished()
+{
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_sendNotifyProgressFinished);
+ checkException(env);
+}
+
+void WebViewCore::viewInvalidate(const WebCore::IntRect& rect)
+{
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_sendViewInvalidate,
+ rect.x(), rect.y(), rect.right(), rect.bottom());
+ checkException(env);
+}
+
+void WebViewCore::contentDraw()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_contentDraw);
+ checkException(env);
+}
+
+void WebViewCore::layersDraw()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_layersDraw);
+ checkException(env);
+}
+
+void WebViewCore::contentInvalidate(const WebCore::IntRect &r)
+{
+ DBG_SET_LOGD("rect={%d,%d,w=%d,h=%d}", r.x(), r.y(), r.width(), r.height());
+ SkIRect rect(r);
+ if (!rect.intersect(0, 0, INT_MAX, INT_MAX))
+ return;
+ m_addInval.op(rect, SkRegion::kUnion_Op);
+ DBG_SET_LOGD("m_addInval={%d,%d,r=%d,b=%d}",
+ m_addInval.getBounds().fLeft, m_addInval.getBounds().fTop,
+ m_addInval.getBounds().fRight, m_addInval.getBounds().fBottom);
+ if (!m_skipContentDraw)
+ contentDraw();
+}
+
+void WebViewCore::contentInvalidateAll()
+{
+ WebCore::FrameView* view = m_mainFrame->view();
+ contentInvalidate(WebCore::IntRect(0, 0,
+ view->contentsWidth(), view->contentsHeight()));
+}
+
+void WebViewCore::offInvalidate(const WebCore::IntRect &r)
+{
+ // FIXME: these invalidates are offscreen, and can be throttled or
+ // deferred until the area is visible. For now, treat them as
+ // regular invals so that drawing happens (inefficiently) for now.
+ contentInvalidate(r);
+}
+
+static int pin_pos(int x, int width, int targetWidth)
+{
+ if (x + width > targetWidth)
+ x = targetWidth - width;
+ if (x < 0)
+ x = 0;
+ return x;
+}
+
+void WebViewCore::didFirstLayout()
+{
+ DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+
+ WebCore::FrameLoader* loader = m_mainFrame->loader();
+ const WebCore::KURL& url = loader->url();
+ if (url.isEmpty())
+ return;
+ LOGV("::WebCore:: didFirstLayout %s", url.string().ascii().data());
+
+ WebCore::FrameLoadType loadType = loader->loadType();
+
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_didFirstLayout,
+ loadType == WebCore::FrameLoadTypeStandard
+ // When redirect with locked history, we would like to reset the
+ // scale factor. This is important for www.yahoo.com as it is
+ // redirected to www.yahoo.com/?rs=1 on load.
+ || loadType == WebCore::FrameLoadTypeRedirectWithLockedBackForwardList);
+ checkException(env);
+
+ DBG_NAV_LOG("call updateFrameCache");
+ m_check_domtree_version = false;
+ updateFrameCache();
+ m_history.setDidFirstLayout(true);
+}
+
+void WebViewCore::updateViewport()
+{
+ DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_updateViewport);
+ checkException(env);
+}
+
+void WebViewCore::restoreScale(float scale, float textWrapScale)
+{
+ DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_restoreScale, scale, textWrapScale);
+ checkException(env);
+}
+
+void WebViewCore::needTouchEvents(bool need)
+{
+ DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+
+#if ENABLE(TOUCH_EVENTS)
+ if (m_forwardingTouchEvents == need)
+ return;
+
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_needTouchEvents, need);
+ checkException(env);
+
+ m_forwardingTouchEvents = need;
+#endif
+}
+
+void WebViewCore::requestKeyboardWithSelection(const WebCore::Node* node,
+ int selStart, int selEnd)
+{
+ DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_requestKeyboardWithSelection,
+ reinterpret_cast<int>(node), selStart, selEnd, m_textGeneration);
+ checkException(env);
+}
+
+void WebViewCore::requestKeyboard(bool showKeyboard)
+{
+ DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_requestKeyboard, showKeyboard);
+ checkException(env);
+}
+
+void WebViewCore::notifyProgressFinished()
+{
+ m_check_domtree_version = true;
+ sendNotifyProgressFinished();
+}
+
+void WebViewCore::doMaxScroll(CacheBuilder::Direction dir)
+{
+ int dx = 0, dy = 0;
+
+ switch (dir) {
+ case CacheBuilder::LEFT:
+ dx = -m_maxXScroll;
+ break;
+ case CacheBuilder::UP:
+ dy = -m_maxYScroll;
+ break;
+ case CacheBuilder::RIGHT:
+ dx = m_maxXScroll;
+ break;
+ case CacheBuilder::DOWN:
+ dy = m_maxYScroll;
+ break;
+ case CacheBuilder::UNINITIALIZED:
+ default:
+ LOG_ASSERT(0, "unexpected focus selector");
+ }
+ WebCore::FrameView* view = m_mainFrame->view();
+ this->scrollTo(view->scrollX() + dx, view->scrollY() + dy, true);
+}
+
+void WebViewCore::setScrollOffset(int moveGeneration, bool sendScrollEvent, int dx, int dy)
+{
+ DBG_NAV_LOGD("{%d,%d} m_scrollOffset=(%d,%d), sendScrollEvent=%d", dx, dy,
+ m_scrollOffsetX, m_scrollOffsetY, sendScrollEvent);
+ if (m_scrollOffsetX != dx || m_scrollOffsetY != dy) {
+ m_scrollOffsetX = dx;
+ m_scrollOffsetY = dy;
+ // The visible rect is located within our coordinate space so it
+ // contains the actual scroll position. Setting the location makes hit
+ // testing work correctly.
+ m_mainFrame->view()->platformWidget()->setLocation(m_scrollOffsetX,
+ m_scrollOffsetY);
+ if (sendScrollEvent) {
+ m_mainFrame->eventHandler()->sendScrollEvent();
+
+ // Only update history position if it's user scrolled.
+ // Update history item to reflect the new scroll position.
+ // This also helps save the history information when the browser goes to
+ // background, so scroll position will be restored if browser gets
+ // killed while in background.
+ WebCore::HistoryController* history = m_mainFrame->loader()->history();
+ // Because the history item saving could be heavy for large sites and
+ // scrolling can generate lots of small scroll offset, the following code
+ // reduces the saving frequency.
+ static const int MIN_SCROLL_DIFF = 32;
+ if (history->currentItem()) {
+ WebCore::IntPoint currentPoint = history->currentItem()->scrollPoint();
+ if (std::abs(currentPoint.x() - dx) >= MIN_SCROLL_DIFF ||
+ std::abs(currentPoint.y() - dy) >= MIN_SCROLL_DIFF) {
+ history->saveScrollPositionAndViewStateToItem(history->currentItem());
+ }
+ }
+ }
+
+ // update the currently visible screen
+ sendPluginVisibleScreen();
+ }
+ gCursorBoundsMutex.lock();
+ bool hasCursorBounds = m_hasCursorBounds;
+ Frame* frame = (Frame*) m_cursorFrame;
+ IntPoint location = m_cursorLocation;
+ gCursorBoundsMutex.unlock();
+ if (!hasCursorBounds)
+ return;
+ moveMouseIfLatest(moveGeneration, frame, location.x(), location.y());
+}
+
+void WebViewCore::setGlobalBounds(int x, int y, int h, int v)
+{
+ DBG_NAV_LOGD("{%d,%d}", x, y);
+ m_mainFrame->view()->platformWidget()->setWindowBounds(x, y, h, v);
+}
+
+void WebViewCore::setSizeScreenWidthAndScale(int width, int height,
+ int textWrapWidth, float scale, int screenWidth, int screenHeight,
+ int anchorX, int anchorY, bool ignoreHeight)
+{
+ WebCoreViewBridge* window = m_mainFrame->view()->platformWidget();
+ int ow = window->width();
+ int oh = window->height();
+ int osw = m_screenWidth;
+ int osh = m_screenHeight;
+ int otw = m_textWrapWidth;
+ float oldScale = m_scale;
+ DBG_NAV_LOGD("old:(w=%d,h=%d,sw=%d,scale=%g) new:(w=%d,h=%d,sw=%d,scale=%g)",
+ ow, oh, osw, m_scale, width, height, screenWidth, scale);
+ m_screenWidth = screenWidth;
+ m_screenHeight = screenHeight;
+ m_textWrapWidth = textWrapWidth;
+ if (scale >= 0) // negative means keep the current scale
+ m_scale = scale;
+ m_maxXScroll = screenWidth >> 2;
+ m_maxYScroll = m_maxXScroll * height / width;
+ // Don't reflow if the diff is small.
+ const bool reflow = otw && textWrapWidth &&
+ ((float) abs(otw - textWrapWidth) / textWrapWidth) >= 0.01f;
+
+ // When the screen size change, fixed positioned element should be updated.
+ // This is supposed to be light weighted operation without a full layout.
+ if (osh != screenHeight || osw != screenWidth)
+ m_mainFrame->view()->updatePositionedObjects();
+
+ if (ow != width || (!ignoreHeight && oh != height) || reflow) {
+ WebCore::RenderObject *r = m_mainFrame->contentRenderer();
+ DBG_NAV_LOGD("renderer=%p view=(w=%d,h=%d)", r,
+ screenWidth, screenHeight);
+ if (r) {
+ WebCore::IntPoint anchorPoint = WebCore::IntPoint(anchorX, anchorY);
+ DBG_NAV_LOGD("anchorX=%d anchorY=%d", anchorX, anchorY);
+ RefPtr<WebCore::Node> node;
+ WebCore::IntRect bounds;
+ WebCore::IntPoint offset;
+ // If the text wrap changed, it is probably zoom change or
+ // orientation change. Try to keep the anchor at the same place.
+ if (otw && textWrapWidth && otw != textWrapWidth &&
+ (anchorX != 0 || anchorY != 0)) {
+ WebCore::HitTestResult hitTestResult =
+ m_mainFrame->eventHandler()->hitTestResultAtPoint(
+ anchorPoint, false);
+ node = hitTestResult.innerNode();
+ }
+ if (node) {
+ bounds = node->getRect();
+ DBG_NAV_LOGD("ob:(x=%d,y=%d,w=%d,h=%d)",
+ bounds.x(), bounds.y(), bounds.width(), bounds.height());
+ // sites like nytimes.com insert a non-standard tag <nyt_text>
+ // in the html. If it is the HitTestResult, it may have zero
+ // width and height. In this case, use its parent node.
+ if (bounds.width() == 0) {
+ node = node->parentOrHostNode();
+ if (node) {
+ bounds = node->getRect();
+ DBG_NAV_LOGD("found a zero width node and use its parent, whose ob:(x=%d,y=%d,w=%d,h=%d)",
+ bounds.x(), bounds.y(), bounds.width(), bounds.height());
+ }
+ }
+ }
+
+ // Set the size after finding the old anchor point as
+ // hitTestResultAtPoint causes a layout.
+ window->setSize(width, height);
+ window->setVisibleSize(screenWidth, screenHeight);
+ if (width != screenWidth) {
+ m_mainFrame->view()->setUseFixedLayout(true);
+ m_mainFrame->view()->setFixedLayoutSize(IntSize(width, height));
+ } else {
+ m_mainFrame->view()->setUseFixedLayout(false);
+ }
+ r->setNeedsLayoutAndPrefWidthsRecalc();
+ m_mainFrame->view()->forceLayout();
+
+ // scroll to restore current screen center
+ if (node) {
+ const WebCore::IntRect& newBounds = node->getRect();
+ DBG_NAV_LOGD("nb:(x=%d,y=%d,w=%d,"
+ "h=%d)", newBounds.x(), newBounds.y(),
+ newBounds.width(), newBounds.height());
+ if ((osw && osh && bounds.width() && bounds.height())
+ && (bounds != newBounds)) {
+ WebCore::FrameView* view = m_mainFrame->view();
+ // force left align if width is not changed while height changed.
+ // the anchorPoint is probably at some white space in the node
+ // which is affected by text wrap around the screen width.
+ const bool leftAlign = (otw != textWrapWidth)
+ && (bounds.width() == newBounds.width())
+ && (bounds.height() != newBounds.height());
+ const float xPercentInDoc =
+ leftAlign ? 0.0 : (float) (anchorX - bounds.x()) / bounds.width();
+ const float xPercentInView =
+ leftAlign ? 0.0 : (float) (anchorX - m_scrollOffsetX) / osw;
+ const float yPercentInDoc = (float) (anchorY - bounds.y()) / bounds.height();
+ const float yPercentInView = (float) (anchorY - m_scrollOffsetY) / osh;
+ showRect(newBounds.x(), newBounds.y(), newBounds.width(),
+ newBounds.height(), view->contentsWidth(),
+ view->contentsHeight(),
+ xPercentInDoc, xPercentInView,
+ yPercentInDoc, yPercentInView);
+ }
+ }
+ }
+ } else {
+ window->setSize(width, height);
+ window->setVisibleSize(screenWidth, screenHeight);
+ m_mainFrame->view()->resize(width, height);
+ if (width != screenWidth) {
+ m_mainFrame->view()->setUseFixedLayout(true);
+ m_mainFrame->view()->setFixedLayoutSize(IntSize(width, height));
+ } else {
+ m_mainFrame->view()->setUseFixedLayout(false);
+ }
+ }
+
+ // update the currently visible screen as perceived by the plugin
+ sendPluginVisibleScreen();
+}
+
+void WebViewCore::dumpDomTree(bool useFile)
+{
+#ifdef ANDROID_DOM_LOGGING
+ if (useFile)
+ gDomTreeFile = fopen(DOM_TREE_LOG_FILE, "w");
+ m_mainFrame->document()->showTreeForThis();
+ if (gDomTreeFile) {
+ fclose(gDomTreeFile);
+ gDomTreeFile = 0;
+ }
+#endif
+}
+
+void WebViewCore::dumpRenderTree(bool useFile)
+{
+#ifdef ANDROID_DOM_LOGGING
+ WTF::CString renderDump = WebCore::externalRepresentation(m_mainFrame).utf8();
+ const char* data = renderDump.data();
+ if (useFile) {
+ gRenderTreeFile = fopen(RENDER_TREE_LOG_FILE, "w");
+ DUMP_RENDER_LOGD("%s", data);
+ fclose(gRenderTreeFile);
+ gRenderTreeFile = 0;
+ } else {
+ // adb log can only output 1024 characters, so write out line by line.
+ // exclude '\n' as adb log adds it for each output.
+ int length = renderDump.length();
+ for (int i = 0, last = 0; i < length; i++) {
+ if (data[i] == '\n') {
+ if (i != last)
+ DUMP_RENDER_LOGD("%.*s", (i - last), &(data[last]));
+ last = i + 1;
+ }
+ }
+ }
+#endif
+}
+
+void WebViewCore::dumpNavTree()
+{
+#if DUMP_NAV_CACHE
+ cacheBuilder().mDebug.print();
+#endif
+}
+
+HTMLElement* WebViewCore::retrieveElement(int x, int y,
+ const QualifiedName& tagName)
+{
+ HitTestResult hitTestResult = m_mainFrame->eventHandler()
+ ->hitTestResultAtPoint(IntPoint(x, y), false, false,
+ DontHitTestScrollbars, HitTestRequest::Active | HitTestRequest::ReadOnly,
+ IntSize(1, 1));
+ if (!hitTestResult.innerNode() || !hitTestResult.innerNode()->inDocument()) {
+ LOGE("Should not happen: no in document Node found");
+ return 0;
+ }
+ const ListHashSet<RefPtr<Node> >& list = hitTestResult.rectBasedTestResult();
+ if (list.isEmpty()) {
+ LOGE("Should not happen: no rect-based-test nodes found");
+ return 0;
+ }
+ Node* node = hitTestResult.innerNode();
+ Node* element = node;
+ while (element && (!element->isElementNode()
+ || !element->hasTagName(tagName))) {
+ element = element->parentNode();
+ }
+ DBG_NAV_LOGD("node=%p element=%p x=%d y=%d nodeName=%s tagName=%s", node,
+ element, x, y, node->nodeName().utf8().data(),
+ element ? ((Element*) element)->tagName().utf8().data() : "<none>");
+ return static_cast<WebCore::HTMLElement*>(element);
+}
+
+HTMLAnchorElement* WebViewCore::retrieveAnchorElement(int x, int y)
+{
+ return static_cast<HTMLAnchorElement*>
+ (retrieveElement(x, y, HTMLNames::aTag));
+}
+
+HTMLImageElement* WebViewCore::retrieveImageElement(int x, int y)
+{
+ return static_cast<HTMLImageElement*>
+ (retrieveElement(x, y, HTMLNames::imgTag));
+}
+
+WTF::String WebViewCore::retrieveHref(int x, int y)
+{
+ WebCore::HTMLAnchorElement* anchor = retrieveAnchorElement(x, y);
+ return anchor ? anchor->href() : WTF::String();
+}
+
+WTF::String WebViewCore::retrieveAnchorText(int x, int y)
+{
+ WebCore::HTMLAnchorElement* anchor = retrieveAnchorElement(x, y);
+ return anchor ? anchor->text() : WTF::String();
+}
+
+WTF::String WebViewCore::retrieveImageSource(int x, int y)
+{
+ HTMLImageElement* image = retrieveImageElement(x, y);
+ return image ? image->src().string() : WTF::String();
+}
+
+WTF::String WebViewCore::requestLabel(WebCore::Frame* frame,
+ WebCore::Node* node)
+{
+ if (node && CacheBuilder::validNode(m_mainFrame, frame, node)) {
+ RefPtr<WebCore::NodeList> list = node->document()->getElementsByTagName("label");
+ unsigned length = list->length();
+ for (unsigned i = 0; i < length; i++) {
+ WebCore::HTMLLabelElement* label = static_cast<WebCore::HTMLLabelElement*>(
+ list->item(i));
+ if (label->control() == node) {
+ Node* node = label;
+ String result;
+ while ((node = node->traverseNextNode(label))) {
+ if (node->isTextNode()) {
+ Text* textNode = static_cast<Text*>(node);
+ result += textNode->dataImpl();
+ }
+ }
+ return result;
+ }
+ }
+ }
+ return WTF::String();
+}
+
+static bool isContentEditable(const WebCore::Node* node)
+{
+ if (!node) return false;
+ return node->document()->frame()->selection()->isContentEditable();
+}
+
+// Returns true if the node is a textfield, textarea, or contentEditable
+static bool isTextInput(const WebCore::Node* node)
+{
+ if (isContentEditable(node))
+ return true;
+ if (!node)
+ return false;
+ WebCore::RenderObject* renderer = node->renderer();
+ return renderer && (renderer->isTextField() || renderer->isTextArea());
+}
+
+void WebViewCore::revealSelection()
+{
+ WebCore::Node* focus = currentFocus();
+ if (!focus)
+ return;
+ if (!isTextInput(focus))
+ return;
+ WebCore::Frame* focusedFrame = focus->document()->frame();
+ if (!focusedFrame->page()->focusController()->isActive())
+ return;
+ focusedFrame->selection()->revealSelection(ScrollAlignment::alignToEdgeIfNeeded);
+}
+
+void WebViewCore::updateCacheOnNodeChange()
+{
+ gCursorBoundsMutex.lock();
+ bool hasCursorBounds = m_hasCursorBounds;
+ Frame* frame = (Frame*) m_cursorFrame;
+ Node* node = (Node*) m_cursorNode;
+ IntRect bounds = m_cursorHitBounds;
+ gCursorBoundsMutex.unlock();
+ if (!hasCursorBounds || !node)
+ return;
+ if (CacheBuilder::validNode(m_mainFrame, frame, node)) {
+ RenderObject* renderer = node->renderer();
+ if (renderer && renderer->style()->visibility() != HIDDEN) {
+ IntRect absBox = renderer->absoluteBoundingBoxRect();
+ int globalX, globalY;
+ CacheBuilder::GetGlobalOffset(frame, &globalX, &globalY);
+ absBox.move(globalX, globalY);
+ if (absBox == bounds)
+ return;
+ DBG_NAV_LOGD("absBox=(%d,%d,%d,%d) bounds=(%d,%d,%d,%d)",
+ absBox.x(), absBox.y(), absBox.width(), absBox.height(),
+ bounds.x(), bounds.y(), bounds.width(), bounds.height());
+ }
+ }
+ DBG_NAV_LOGD("updateFrameCache node=%p", node);
+ updateFrameCache();
+}
+
+void WebViewCore::updateFrameCache()
+{
+ if (!m_frameCacheOutOfDate) {
+ DBG_NAV_LOG("!m_frameCacheOutOfDate");
+ return;
+ }
+
+ // If there is a pending style recalculation, do not update the frame cache.
+ // Until the recalculation is complete, there may be internal objects that
+ // are in an inconsistent state (such as font pointers).
+ // In any event, there's not much point to updating the cache while a style
+ // recalculation is pending, since it will simply have to be updated again
+ // once the recalculation is complete.
+ // TODO: Do we need to reschedule an update for after the style is recalculated?
+ if (m_mainFrame && m_mainFrame->document() && m_mainFrame->document()->isPendingStyleRecalc()) {
+ LOGW("updateFrameCache: pending style recalc, ignoring.");
+ return;
+ }
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreBuildNavTimeCounter);
+#endif
+ m_frameCacheOutOfDate = false;
+#if DEBUG_NAV_UI
+ m_now = SkTime::GetMSecs();
+#endif
+ m_temp = new CachedRoot();
+ m_temp->init(m_mainFrame, &m_history);
+#if USE(ACCELERATED_COMPOSITING)
+ GraphicsLayerAndroid* graphicsLayer = graphicsRootLayer();
+ if (graphicsLayer)
+ m_temp->setRootLayer(graphicsLayer->contentLayer());
+#endif
+ CacheBuilder& builder = cacheBuilder();
+ WebCore::Settings* settings = m_mainFrame->page()->settings();
+ builder.allowAllTextDetection();
+#ifdef ANDROID_META_SUPPORT
+ if (settings) {
+ if (!settings->formatDetectionAddress())
+ builder.disallowAddressDetection();
+ if (!settings->formatDetectionEmail())
+ builder.disallowEmailDetection();
+ if (!settings->formatDetectionTelephone())
+ builder.disallowPhoneDetection();
+ }
+#endif
+ builder.buildCache(m_temp);
+ m_tempPict = new SkPicture();
+ recordPicture(m_tempPict);
+ m_temp->setPicture(m_tempPict);
+ m_temp->setTextGeneration(m_textGeneration);
+ WebCoreViewBridge* window = m_mainFrame->view()->platformWidget();
+ m_temp->setVisibleRect(WebCore::IntRect(m_scrollOffsetX,
+ m_scrollOffsetY, window->width(), window->height()));
+ gFrameCacheMutex.lock();
+ delete m_frameCacheKit;
+ delete m_navPictureKit;
+ m_frameCacheKit = m_temp;
+ m_navPictureKit = m_tempPict;
+ m_updatedFrameCache = true;
+#if DEBUG_NAV_UI
+ const CachedNode* cachedFocusNode = m_frameCacheKit->currentFocus();
+ DBG_NAV_LOGD("cachedFocusNode=%d (nodePointer=%p)",
+ cachedFocusNode ? cachedFocusNode->index() : 0,
+ cachedFocusNode ? cachedFocusNode->nodePointer() : 0);
+#endif
+ gFrameCacheMutex.unlock();
+}
+
+void WebViewCore::updateFrameCacheIfLoading()
+{
+ if (!m_check_domtree_version)
+ updateFrameCache();
+}
+
+struct TouchNodeData {
+ Node* mNode;
+ IntRect mBounds;
+};
+
+// get the bounding box of the Node
+static IntRect getAbsoluteBoundingBox(Node* node) {
+ IntRect rect;
+ RenderObject* render = node->renderer();
+ if (render->isRenderInline())
+ rect = toRenderInline(render)->linesVisualOverflowBoundingBox();
+ else if (render->isBox())
+ rect = toRenderBox(render)->visualOverflowRect();
+ else if (render->isText())
+ rect = toRenderText(render)->linesBoundingBox();
+ else
+ LOGE("getAbsoluteBoundingBox failed for node %p, name %s", node, render->renderName());
+ FloatPoint absPos = render->localToAbsolute();
+ rect.move(absPos.x(), absPos.y());
+ return rect;
+}
+
+// get the highlight rectangles for the touch point (x, y) with the slop
+Vector<IntRect> WebViewCore::getTouchHighlightRects(int x, int y, int slop)
+{
+ Vector<IntRect> rects;
+ m_mousePos = IntPoint(x - m_scrollOffsetX, y - m_scrollOffsetY);
+ HitTestResult hitTestResult = m_mainFrame->eventHandler()->hitTestResultAtPoint(IntPoint(x, y),
+ false, false, DontHitTestScrollbars, HitTestRequest::Active | HitTestRequest::ReadOnly, IntSize(slop, slop));
+ if (!hitTestResult.innerNode() || !hitTestResult.innerNode()->inDocument()) {
+ LOGE("Should not happen: no in document Node found");
+ return rects;
+ }
+ const ListHashSet<RefPtr<Node> >& list = hitTestResult.rectBasedTestResult();
+ if (list.isEmpty()) {
+ LOGE("Should not happen: no rect-based-test nodes found");
+ return rects;
+ }
+ Frame* frame = hitTestResult.innerNode()->document()->frame();
+ Vector<TouchNodeData> nodeDataList;
+ ListHashSet<RefPtr<Node> >::const_iterator last = list.end();
+ for (ListHashSet<RefPtr<Node> >::const_iterator it = list.begin(); it != last; ++it) {
+ // TODO: it seems reasonable to not search across the frame. Isn't it?
+ // if the node is not in the same frame as the innerNode, skip it
+ if (it->get()->document()->frame() != frame)
+ continue;
+ // traverse up the tree to find the first node that needs highlight
+ bool found = false;
+ Node* eventNode = it->get();
+ while (eventNode) {
+ RenderObject* render = eventNode->renderer();
+ if (render->isBody() || render->isRenderView())
+ break;
+ if (eventNode->supportsFocus()
+ || eventNode->hasEventListeners(eventNames().clickEvent)
+ || eventNode->hasEventListeners(eventNames().mousedownEvent)
+ || eventNode->hasEventListeners(eventNames().mouseupEvent)) {
+ found = true;
+ break;
+ }
+ // the nodes in the rectBasedTestResult() are ordered based on z-index during hit testing.
+ // so do not search for the eventNode across explicit z-index border.
+ // TODO: this is a hard one to call. z-index is quite complicated as its value only
+ // matters when you compare two RenderLayer in the same hierarchy level. e.g. in
+ // the following example, "b" is on the top as its z level is the highest. even "c"
+ // has 100 as z-index, it is still below "d" as its parent has the same z-index as
+ // "d" and logically before "d". Of course "a" is the lowest in the z level.
+ //
+ // z-index:auto "a"
+ // z-index:2 "b"
+ // z-index:1
+ // z-index:100 "c"
+ // z-index:1 "d"
+ //
+ // If the fat point touches everyone, the order in the list should be "b", "d", "c"
+ // and "a". When we search for the event node for "b", we really don't want "a" as
+ // in the z-order it is behind everything else.
+ if (!render->style()->hasAutoZIndex())
+ break;
+ eventNode = eventNode->parentNode();
+ }
+ // didn't find any eventNode, skip it
+ if (!found)
+ continue;
+ // first quick check whether it is a duplicated node before computing bounding box
+ Vector<TouchNodeData>::const_iterator nlast = nodeDataList.end();
+ for (Vector<TouchNodeData>::const_iterator n = nodeDataList.begin(); n != nlast; ++n) {
+ // found the same node, skip it
+ if (eventNode == n->mNode) {
+ found = false;
+ break;
+ }
+ }
+ if (!found)
+ continue;
+ // next check whether the node is fully covered by or fully covering another node.
+ found = false;
+ IntRect rect = getAbsoluteBoundingBox(eventNode);
+ if (rect.isEmpty()) {
+ // if the node's bounds is empty and it is not a ContainerNode, skip it.
+ if (!eventNode->isContainerNode())
+ continue;
+ // if the node's children are all positioned objects, its bounds can be empty.
+ // Walk through the children to find the bounding box.
+ Node* child = static_cast<const ContainerNode*>(eventNode)->firstChild();
+ while (child) {
+ IntRect childrect;
+ if (child->renderer())
+ childrect = getAbsoluteBoundingBox(child);
+ if (!childrect.isEmpty()) {
+ rect.unite(childrect);
+ child = child->traverseNextSibling(eventNode);
+ } else
+ child = child->traverseNextNode(eventNode);
+ }
+ }
+ for (int i = nodeDataList.size() - 1; i >= 0; i--) {
+ TouchNodeData n = nodeDataList.at(i);
+ // the new node is enclosing an existing node, skip it
+ if (rect.contains(n.mBounds)) {
+ found = true;
+ break;
+ }
+ // the new node is fully inside an existing node, remove the existing node
+ if (n.mBounds.contains(rect))
+ nodeDataList.remove(i);
+ }
+ if (!found) {
+ TouchNodeData newNode;
+ newNode.mNode = eventNode;
+ newNode.mBounds = rect;
+ nodeDataList.append(newNode);
+ }
+ }
+ if (!nodeDataList.size())
+ return rects;
+ // finally select the node with the largest overlap with the fat point
+ TouchNodeData final;
+ final.mNode = 0;
+ IntPoint docPos = frame->view()->windowToContents(m_mousePos);
+ IntRect testRect(docPos.x() - slop, docPos.y() - slop, 2 * slop + 1, 2 * slop + 1);
+ int area = 0;
+ Vector<TouchNodeData>::const_iterator nlast = nodeDataList.end();
+ for (Vector<TouchNodeData>::const_iterator n = nodeDataList.begin(); n != nlast; ++n) {
+ IntRect rect = n->mBounds;
+ rect.intersect(testRect);
+ int a = rect.width() * rect.height();
+ if (a > area) {
+ final = *n;
+ area = a;
+ }
+ }
+ // now get the node's highlight rectangles in the page coordinate system
+ if (final.mNode) {
+ IntPoint frameAdjust;
+ if (frame != m_mainFrame) {
+ frameAdjust = frame->view()->contentsToWindow(IntPoint());
+ frameAdjust.move(m_scrollOffsetX, m_scrollOffsetY);
+ }
+ if (final.mNode->isLink()) {
+ // most of the links are inline instead of box style. So the bounding box is not
+ // a good representation for the highlights. Get the list of rectangles instead.
+ RenderObject* render = final.mNode->renderer();
+ IntPoint offset = roundedIntPoint(render->localToAbsolute());
+ render->absoluteRects(rects, offset.x() + frameAdjust.x(), offset.y() + frameAdjust.y());
+ bool inside = false;
+ int distance = INT_MAX;
+ int newx = x, newy = y;
+ int i = rects.size();
+ while (i--) {
+ if (rects[i].isEmpty()) {
+ rects.remove(i);
+ continue;
+ }
+ // check whether the point (x, y) is inside one of the rectangles.
+ if (inside)
+ continue;
+ if (rects[i].contains(x, y)) {
+ inside = true;
+ continue;
+ }
+ if (x >= rects[i].x() && x < rects[i].right()) {
+ if (y < rects[i].y()) {
+ if (rects[i].y() - y < distance) {
+ newx = x;
+ newy = rects[i].y();
+ distance = rects[i].y() - y;
+ }
+ } else if (y >= rects[i].bottom()) {
+ if (y - rects[i].bottom() + 1 < distance) {
+ newx = x;
+ newy = rects[i].bottom() - 1;
+ distance = y - rects[i].bottom() + 1;
+ }
+ }
+ } else if (y >= rects[i].y() && y < rects[i].bottom()) {
+ if (x < rects[i].x()) {
+ if (rects[i].x() - x < distance) {
+ newx = rects[i].x();
+ newy = y;
+ distance = rects[i].x() - x;
+ }
+ } else if (x >= rects[i].right()) {
+ if (x - rects[i].right() + 1 < distance) {
+ newx = rects[i].right() - 1;
+ newy = y;
+ distance = x - rects[i].right() + 1;
+ }
+ }
+ }
+ }
+ if (!rects.isEmpty()) {
+ if (!inside) {
+ // if neither x nor y has overlap, just pick the top/left of the first rectangle
+ if (newx == x && newy == y) {
+ newx = rects[0].x();
+ newy = rects[0].y();
+ }
+ m_mousePos.setX(newx - m_scrollOffsetX);
+ m_mousePos.setY(newy - m_scrollOffsetY);
+ DBG_NAV_LOGD("Move x/y from (%d, %d) to (%d, %d) scrollOffset is (%d, %d)",
+ x, y, m_mousePos.x() + m_scrollOffsetX, m_mousePos.y() + m_scrollOffsetY,
+ m_scrollOffsetX, m_scrollOffsetY);
+ }
+ return rects;
+ }
+ }
+ IntRect rect = final.mBounds;
+ rect.move(frameAdjust.x(), frameAdjust.y());
+ rects.append(rect);
+ // adjust m_mousePos if it is not inside the returned highlight rectangle
+ testRect.move(frameAdjust.x(), frameAdjust.y());
+ testRect.intersect(rect);
+ if (!testRect.contains(x, y)) {
+ m_mousePos = testRect.center();
+ m_mousePos.move(-m_scrollOffsetX, -m_scrollOffsetY);
+ DBG_NAV_LOGD("Move x/y from (%d, %d) to (%d, %d) scrollOffset is (%d, %d)",
+ x, y, m_mousePos.x() + m_scrollOffsetX, m_mousePos.y() + m_scrollOffsetY,
+ m_scrollOffsetX, m_scrollOffsetY);
+ }
+ }
+ return rects;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void WebViewCore::addPlugin(PluginWidgetAndroid* w)
+{
+// SkDebugf("----------- addPlugin %p", w);
+ /* The plugin must be appended to the end of the array. This ensures that if
+ the plugin is added while iterating through the array (e.g. sendEvent(...))
+ that the iteration process is not corrupted.
+ */
+ *m_plugins.append() = w;
+}
+
+void WebViewCore::removePlugin(PluginWidgetAndroid* w)
+{
+// SkDebugf("----------- removePlugin %p", w);
+ int index = m_plugins.find(w);
+ if (index < 0) {
+ SkDebugf("--------------- pluginwindow not found! %p\n", w);
+ } else {
+ m_plugins.removeShuffle(index);
+ }
+}
+
+bool WebViewCore::isPlugin(PluginWidgetAndroid* w) const
+{
+ return m_plugins.find(w) >= 0;
+}
+
+void WebViewCore::invalPlugin(PluginWidgetAndroid* w)
+{
+ const double PLUGIN_INVAL_DELAY = 1.0 / 60;
+
+ if (!m_pluginInvalTimer.isActive()) {
+ m_pluginInvalTimer.startOneShot(PLUGIN_INVAL_DELAY);
+ }
+}
+
+void WebViewCore::drawPlugins()
+{
+ SkRegion inval; // accumualte what needs to be redrawn
+ PluginWidgetAndroid** iter = m_plugins.begin();
+ PluginWidgetAndroid** stop = m_plugins.end();
+
+ for (; iter < stop; ++iter) {
+ PluginWidgetAndroid* w = *iter;
+ SkIRect dirty;
+ if (w->isDirty(&dirty)) {
+ w->draw();
+ inval.op(dirty, SkRegion::kUnion_Op);
+ }
+ }
+
+ if (!inval.isEmpty()) {
+ // inval.getBounds() is our rectangle
+ const SkIRect& bounds = inval.getBounds();
+ WebCore::IntRect r(bounds.fLeft, bounds.fTop,
+ bounds.width(), bounds.height());
+ this->viewInvalidate(r);
+ }
+}
+
+void WebViewCore::notifyPluginsOnFrameLoad(const Frame* frame) {
+ // if frame is the parent then notify all plugins
+ if (!frame->tree()->parent()) {
+ // trigger an event notifying the plugins that the page has loaded
+ ANPEvent event;
+ SkANP::InitEvent(&event, kLifecycle_ANPEventType);
+ event.data.lifecycle.action = kOnLoad_ANPLifecycleAction;
+ sendPluginEvent(event);
+ // trigger the on/off screen notification if the page was reloaded
+ sendPluginVisibleScreen();
+ }
+ // else if frame's parent has completed
+ else if (!frame->tree()->parent()->loader()->isLoading()) {
+ // send to all plugins who have this frame in their heirarchy
+ PluginWidgetAndroid** iter = m_plugins.begin();
+ PluginWidgetAndroid** stop = m_plugins.end();
+ for (; iter < stop; ++iter) {
+ Frame* currentFrame = (*iter)->pluginView()->parentFrame();
+ while (currentFrame) {
+ if (frame == currentFrame) {
+ ANPEvent event;
+ SkANP::InitEvent(&event, kLifecycle_ANPEventType);
+ event.data.lifecycle.action = kOnLoad_ANPLifecycleAction;
+ (*iter)->sendEvent(event);
+
+ // trigger the on/off screen notification if the page was reloaded
+ ANPRectI visibleRect;
+ getVisibleScreen(visibleRect);
+ (*iter)->setVisibleScreen(visibleRect, m_scale);
+
+ break;
+ }
+ currentFrame = currentFrame->tree()->parent();
+ }
+ }
+ }
+}
+
+void WebViewCore::getVisibleScreen(ANPRectI& visibleRect)
+{
+ visibleRect.left = m_scrollOffsetX;
+ visibleRect.top = m_scrollOffsetY;
+ visibleRect.right = m_scrollOffsetX + m_screenWidth;
+ visibleRect.bottom = m_scrollOffsetY + m_screenHeight;
+}
+
+void WebViewCore::sendPluginVisibleScreen()
+{
+ /* We may want to cache the previous values and only send the notification
+ to the plugin in the event that one of the values has changed.
+ */
+
+ ANPRectI visibleRect;
+ getVisibleScreen(visibleRect);
+
+ PluginWidgetAndroid** iter = m_plugins.begin();
+ PluginWidgetAndroid** stop = m_plugins.end();
+ for (; iter < stop; ++iter) {
+ (*iter)->setVisibleScreen(visibleRect, m_scale);
+ }
+}
+
+void WebViewCore::sendPluginEvent(const ANPEvent& evt)
+{
+ /* The list of plugins may be manipulated as we iterate through the list.
+ This implementation allows for the addition of new plugins during an
+ iteration, but may fail if a plugin is removed. Currently, there are not
+ any use cases where a plugin is deleted while processing this loop, but
+ if it does occur we will have to use an alternate data structure and/or
+ iteration mechanism.
+ */
+ for (int x = 0; x < m_plugins.count(); x++) {
+ m_plugins[x]->sendEvent(evt);
+ }
+}
+
+PluginWidgetAndroid* WebViewCore::getPluginWidget(NPP npp)
+{
+ PluginWidgetAndroid** iter = m_plugins.begin();
+ PluginWidgetAndroid** stop = m_plugins.end();
+ for (; iter < stop; ++iter) {
+ if ((*iter)->pluginView()->instance() == npp) {
+ return (*iter);
+ }
+ }
+ return 0;
+}
+
+static PluginView* nodeIsPlugin(Node* node) {
+ RenderObject* renderer = node->renderer();
+ if (renderer && renderer->isWidget()) {
+ Widget* widget = static_cast<RenderWidget*>(renderer)->widget();
+ if (widget && widget->isPluginView())
+ return static_cast<PluginView*>(widget);
+ }
+ return 0;
+}
+
+Node* WebViewCore::cursorNodeIsPlugin() {
+ gCursorBoundsMutex.lock();
+ bool hasCursorBounds = m_hasCursorBounds;
+ Frame* frame = (Frame*) m_cursorFrame;
+ Node* node = (Node*) m_cursorNode;
+ gCursorBoundsMutex.unlock();
+ if (hasCursorBounds && CacheBuilder::validNode(m_mainFrame, frame, node)
+ && nodeIsPlugin(node)) {
+ return node;
+ }
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void WebViewCore::moveMouseIfLatest(int moveGeneration,
+ WebCore::Frame* frame, int x, int y)
+{
+ DBG_NAV_LOGD("m_moveGeneration=%d moveGeneration=%d"
+ " frame=%p x=%d y=%d",
+ m_moveGeneration, moveGeneration, frame, x, y);
+ if (m_moveGeneration > moveGeneration) {
+ DBG_NAV_LOGD("m_moveGeneration=%d > moveGeneration=%d",
+ m_moveGeneration, moveGeneration);
+ return; // short-circuit if a newer move has already been generated
+ }
+ m_lastGeneration = moveGeneration;
+ moveMouse(frame, x, y);
+}
+
+void WebViewCore::moveFocus(WebCore::Frame* frame, WebCore::Node* node)
+{
+ DBG_NAV_LOGD("frame=%p node=%p", frame, node);
+ if (!node || !CacheBuilder::validNode(m_mainFrame, frame, node)
+ || !node->isElementNode())
+ return;
+ // Code borrowed from FocusController::advanceFocus
+ WebCore::FocusController* focusController
+ = m_mainFrame->page()->focusController();
+ WebCore::Document* oldDoc
+ = focusController->focusedOrMainFrame()->document();
+ if (oldDoc->focusedNode() == node)
+ return;
+ if (node->document() != oldDoc)
+ oldDoc->setFocusedNode(0);
+ focusController->setFocusedFrame(frame);
+ static_cast<WebCore::Element*>(node)->focus(false);
+}
+
+// Update mouse position
+void WebViewCore::moveMouse(WebCore::Frame* frame, int x, int y)
+{
+ DBG_NAV_LOGD("frame=%p x=%d y=%d scrollOffset=(%d,%d)", frame,
+ x, y, m_scrollOffsetX, m_scrollOffsetY);
+ if (!frame || !CacheBuilder::validNode(m_mainFrame, frame, 0))
+ frame = m_mainFrame;
+ // mouse event expects the position in the window coordinate
+ m_mousePos = WebCore::IntPoint(x - m_scrollOffsetX, y - m_scrollOffsetY);
+ // validNode will still return true if the node is null, as long as we have
+ // a valid frame. Do not want to make a call on frame unless it is valid.
+ WebCore::PlatformMouseEvent mouseEvent(m_mousePos, m_mousePos,
+ WebCore::NoButton, WebCore::MouseEventMoved, 1, false, false, false,
+ false, WTF::currentTime());
+ frame->eventHandler()->handleMouseMoveEvent(mouseEvent);
+ updateCacheOnNodeChange();
+}
+
+void WebViewCore::setSelection(int start, int end)
+{
+ WebCore::Node* focus = currentFocus();
+ if (!focus)
+ return;
+ WebCore::RenderObject* renderer = focus->renderer();
+ if (!renderer || (!renderer->isTextField() && !renderer->isTextArea()))
+ return;
+ if (start > end) {
+ int temp = start;
+ start = end;
+ end = temp;
+ }
+ // Tell our EditorClient that this change was generated from the UI, so it
+ // does not need to echo it to the UI.
+ EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
+ m_mainFrame->editor()->client());
+ client->setUiGeneratedSelectionChange(true);
+ setSelectionRange(focus, start, end);
+ client->setUiGeneratedSelectionChange(false);
+ WebCore::Frame* focusedFrame = focus->document()->frame();
+ bool isPasswordField = false;
+ if (focus->isElementNode()) {
+ WebCore::Element* element = static_cast<WebCore::Element*>(focus);
+ if (WebCore::InputElement* inputElement = WebCore::toInputElement(element))
+ isPasswordField = static_cast<WebCore::HTMLInputElement*>(inputElement)->isPasswordField();
+ }
+ // For password fields, this is done in the UI side via
+ // bringPointIntoView, since the UI does the drawing.
+ if (renderer->isTextArea() || !isPasswordField)
+ revealSelection();
+}
+
+String WebViewCore::modifySelection(const int direction, const int axis)
+{
+ DOMSelection* selection = m_mainFrame->domWindow()->getSelection();
+ if (selection->rangeCount() > 1)
+ selection->removeAllRanges();
+ switch (axis) {
+ case AXIS_CHARACTER:
+ case AXIS_WORD:
+ case AXIS_SENTENCE:
+ return modifySelectionTextNavigationAxis(selection, direction, axis);
+ case AXIS_HEADING:
+ case AXIS_SIBLING:
+ case AXIS_PARENT_FIRST_CHILD:
+ case AXIS_DOCUMENT:
+ return modifySelectionDomNavigationAxis(selection, direction, axis);
+ default:
+ LOGE("Invalid navigation axis: %d", axis);
+ return String();
+ }
+}
+
+void WebViewCore::scrollNodeIntoView(Frame* frame, Node* node)
+{
+ if (!frame || !node)
+ return;
+
+ Element* elementNode = 0;
+
+ // If not an Element, find a visible predecessor
+ // Element to scroll into view.
+ if (!node->isElementNode()) {
+ HTMLElement* body = frame->document()->body();
+ do {
+ if (!node || node == body)
+ return;
+ node = node->parentNode();
+ } while (!node->isElementNode() && !isVisible(node));
+ }
+
+ elementNode = static_cast<Element*>(node);
+ elementNode->scrollIntoViewIfNeeded(true);
+}
+
+String WebViewCore::modifySelectionTextNavigationAxis(DOMSelection* selection, int direction, int axis)
+{
+ Node* body = m_mainFrame->document()->body();
+
+ ExceptionCode ec = 0;
+ String markup;
+
+ // initialize the selection if necessary
+ if (selection->rangeCount() == 0) {
+ if (m_currentNodeDomNavigationAxis
+ && CacheBuilder::validNode(m_mainFrame,
+ m_mainFrame, m_currentNodeDomNavigationAxis)) {
+ PassRefPtr<Range> rangeRef =
+ selection->frame()->document()->createRange();
+ rangeRef->selectNode(m_currentNodeDomNavigationAxis, ec);
+ m_currentNodeDomNavigationAxis = 0;
+ if (ec)
+ return String();
+ selection->addRange(rangeRef.get());
+ } else if (currentFocus()) {
+ selection->setPosition(currentFocus(), 0, ec);
+ } else if (m_cursorNode
+ && CacheBuilder::validNode(m_mainFrame,
+ m_mainFrame, m_cursorNode)) {
+ PassRefPtr<Range> rangeRef =
+ selection->frame()->document()->createRange();
+ rangeRef->selectNode(reinterpret_cast<Node*>(m_cursorNode), ec);
+ if (ec)
+ return String();
+ selection->addRange(rangeRef.get());
+ } else {
+ selection->setPosition(body, 0, ec);
+ }
+ if (ec)
+ return String();
+ }
+
+ // collapse the selection
+ if (direction == DIRECTION_FORWARD)
+ selection->collapseToEnd(ec);
+ else
+ selection->collapseToStart(ec);
+ if (ec)
+ return String();
+
+ // Make sure the anchor node is a text node since we are generating
+ // the markup of the selection which includes the anchor, the focus,
+ // and any crossed nodes. Forcing the condition that the selection
+ // starts and ends on text nodes guarantees symmetric selection markup.
+ // Also this way the text content, rather its container, is highlighted.
+ Node* anchorNode = selection->anchorNode();
+ if (anchorNode->isElementNode()) {
+ // Collapsed selection while moving forward points to the
+ // next unvisited node and while moving backward to the
+ // last visited node.
+ if (direction == DIRECTION_FORWARD)
+ advanceAnchorNode(selection, direction, markup, false, ec);
+ else
+ advanceAnchorNode(selection, direction, markup, true, ec);
+ if (ec)
+ return String();
+ if (!markup.isEmpty())
+ return markup;
+ }
+
+ // If the selection is at the end of a non white space text move
+ // it to the next visible text node with non white space content.
+ // This is a workaround for the selection getting stuck.
+ anchorNode = selection->anchorNode();
+ if (anchorNode->isTextNode()) {
+ if (direction == DIRECTION_FORWARD) {
+ String suffix = anchorNode->textContent().substring(
+ selection->anchorOffset(), caretMaxOffset(anchorNode));
+ // If at the end of non white space text we advance the
+ // anchor node to either an input element or non empty text.
+ if (suffix.stripWhiteSpace().isEmpty()) {
+ advanceAnchorNode(selection, direction, markup, true, ec);
+ }
+ } else {
+ String prefix = anchorNode->textContent().substring(0,
+ selection->anchorOffset());
+ // If at the end of non white space text we advance the
+ // anchor node to either an input element or non empty text.
+ if (prefix.stripWhiteSpace().isEmpty()) {
+ advanceAnchorNode(selection, direction, markup, true, ec);
+ }
+ }
+ if (ec)
+ return String();
+ if (!markup.isEmpty())
+ return markup;
+ }
+
+ // extend the selection
+ String directionStr;
+ if (direction == DIRECTION_FORWARD)
+ directionStr = "forward";
+ else
+ directionStr = "backward";
+
+ String axisStr;
+ if (axis == AXIS_CHARACTER)
+ axisStr = "character";
+ else if (axis == AXIS_WORD)
+ axisStr = "word";
+ else
+ axisStr = "sentence";
+
+ selection->modify("extend", directionStr, axisStr);
+
+ // Make sure the focus node is a text node in order to have the
+ // selection generate symmetric markup because the latter
+ // includes all nodes crossed by the selection. Also this way
+ // the text content, rather its container, is highlighted.
+ Node* focusNode = selection->focusNode();
+ if (focusNode->isElementNode()) {
+ focusNode = getImplicitBoundaryNode(selection->focusNode(),
+ selection->focusOffset(), direction);
+ if (!focusNode)
+ return String();
+ if (direction == DIRECTION_FORWARD) {
+ focusNode = focusNode->traversePreviousSiblingPostOrder(body);
+ if (focusNode && !isContentTextNode(focusNode)) {
+ Node* textNode = traverseNextContentTextNode(focusNode,
+ anchorNode, DIRECTION_BACKWARD);
+ if (textNode)
+ anchorNode = textNode;
+ }
+ if (focusNode && isContentTextNode(focusNode)) {
+ selection->extend(focusNode, caretMaxOffset(focusNode), ec);
+ if (ec)
+ return String();
+ }
+ } else {
+ focusNode = focusNode->traverseNextSibling();
+ if (focusNode && !isContentTextNode(focusNode)) {
+ Node* textNode = traverseNextContentTextNode(focusNode,
+ anchorNode, DIRECTION_FORWARD);
+ if (textNode)
+ anchorNode = textNode;
+ }
+ if (anchorNode && isContentTextNode(anchorNode)) {
+ selection->extend(focusNode, 0, ec);
+ if (ec)
+ return String();
+ }
+ }
+ }
+
+ // Enforce that the selection does not cross anchor boundaries. This is
+ // a workaround for the asymmetric behavior of WebKit while crossing
+ // anchors.
+ anchorNode = getImplicitBoundaryNode(selection->anchorNode(),
+ selection->anchorOffset(), direction);
+ focusNode = getImplicitBoundaryNode(selection->focusNode(),
+ selection->focusOffset(), direction);
+ if (anchorNode && focusNode && anchorNode != focusNode) {
+ Node* inputControl = getIntermediaryInputElement(anchorNode, focusNode,
+ direction);
+ if (inputControl) {
+ if (direction == DIRECTION_FORWARD) {
+ if (isDescendantOf(inputControl, anchorNode)) {
+ focusNode = inputControl;
+ } else {
+ focusNode = inputControl->traversePreviousSiblingPostOrder(
+ body);
+ if (!focusNode)
+ focusNode = inputControl;
+ }
+ // We prefer a text node contained in the input element.
+ if (!isContentTextNode(focusNode)) {
+ Node* textNode = traverseNextContentTextNode(focusNode,
+ anchorNode, DIRECTION_BACKWARD);
+ if (textNode)
+ focusNode = textNode;
+ }
+ // If we found text in the input select it.
+ // Otherwise, select the input element itself.
+ if (isContentTextNode(focusNode)) {
+ selection->extend(focusNode, caretMaxOffset(focusNode), ec);
+ } else if (anchorNode != focusNode) {
+ // Note that the focusNode always has parent and that
+ // the offset can be one more that the index of the last
+ // element - this is how WebKit selects such elements.
+ selection->extend(focusNode->parentNode(),
+ focusNode->nodeIndex() + 1, ec);
+ }
+ if (ec)
+ return String();
+ } else {
+ if (isDescendantOf(inputControl, anchorNode)) {
+ focusNode = inputControl;
+ } else {
+ focusNode = inputControl->traverseNextSibling();
+ if (!focusNode)
+ focusNode = inputControl;
+ }
+ // We prefer a text node contained in the input element.
+ if (!isContentTextNode(focusNode)) {
+ Node* textNode = traverseNextContentTextNode(focusNode,
+ anchorNode, DIRECTION_FORWARD);
+ if (textNode)
+ focusNode = textNode;
+ }
+ // If we found text in the input select it.
+ // Otherwise, select the input element itself.
+ if (isContentTextNode(focusNode)) {
+ selection->extend(focusNode, caretMinOffset(focusNode), ec);
+ } else if (anchorNode != focusNode) {
+ // Note that the focusNode always has parent and that
+ // the offset can be one more that the index of the last
+ // element - this is how WebKit selects such elements.
+ selection->extend(focusNode->parentNode(),
+ focusNode->nodeIndex() + 1, ec);
+ }
+ if (ec)
+ return String();
+ }
+ }
+ }
+
+ // make sure the selection is visible
+ if (direction == DIRECTION_FORWARD)
+ scrollNodeIntoView(m_mainFrame, selection->focusNode());
+ else
+ scrollNodeIntoView(m_mainFrame, selection->anchorNode());
+
+ // format markup for the visible content
+ PassRefPtr<Range> range = selection->getRangeAt(0, ec);
+ if (ec)
+ return String();
+ IntRect bounds = range->boundingBox();
+ selectAt(bounds.center().x(), bounds.center().y());
+ markup = formatMarkup(selection);
+ LOGV("Selection markup: %s", markup.utf8().data());
+
+ return markup;
+}
+
+Node* WebViewCore::getImplicitBoundaryNode(Node* node, unsigned offset, int direction)
+{
+ if (node->offsetInCharacters())
+ return node;
+ if (!node->hasChildNodes())
+ return node;
+ if (offset < node->childNodeCount())
+ return node->childNode(offset);
+ else
+ if (direction == DIRECTION_FORWARD)
+ return node->traverseNextSibling();
+ else
+ return node->traversePreviousNodePostOrder(
+ node->document()->body());
+}
+
+Node* WebViewCore::getNextAnchorNode(Node* anchorNode, bool ignoreFirstNode, int direction)
+{
+ Node* body = 0;
+ Node* currentNode = 0;
+ if (direction == DIRECTION_FORWARD) {
+ if (ignoreFirstNode)
+ currentNode = anchorNode->traverseNextNode(body);
+ else
+ currentNode = anchorNode;
+ } else {
+ body = anchorNode->document()->body();
+ if (ignoreFirstNode)
+ currentNode = anchorNode->traversePreviousSiblingPostOrder(body);
+ else
+ currentNode = anchorNode;
+ }
+ while (currentNode) {
+ if (isContentTextNode(currentNode)
+ || isContentInputElement(currentNode))
+ return currentNode;
+ if (direction == DIRECTION_FORWARD)
+ currentNode = currentNode->traverseNextNode();
+ else
+ currentNode = currentNode->traversePreviousNodePostOrder(body);
+ }
+ return 0;
+}
+
+void WebViewCore::advanceAnchorNode(DOMSelection* selection, int direction,
+ String& markup, bool ignoreFirstNode, ExceptionCode& ec)
+{
+ Node* anchorNode = getImplicitBoundaryNode(selection->anchorNode(),
+ selection->anchorOffset(), direction);
+ if (!anchorNode) {
+ ec = NOT_FOUND_ERR;
+ return;
+ }
+ // If the anchor offset is invalid i.e. the anchor node has no
+ // child with that index getImplicitAnchorNode returns the next
+ // logical node in the current direction. In such a case our
+ // position in the DOM tree was has already been advanced,
+ // therefore we there is no need to do that again.
+ if (selection->anchorNode()->isElementNode()) {
+ unsigned anchorOffset = selection->anchorOffset();
+ unsigned childNodeCount = selection->anchorNode()->childNodeCount();
+ if (anchorOffset >= childNodeCount)
+ ignoreFirstNode = false;
+ }
+ // Find the next anchor node given our position in the DOM and
+ // whether we want the current node to be considered as well.
+ Node* nextAnchorNode = getNextAnchorNode(anchorNode, ignoreFirstNode,
+ direction);
+ if (!nextAnchorNode) {
+ ec = NOT_FOUND_ERR;
+ return;
+ }
+ if (nextAnchorNode->isElementNode()) {
+ // If this is an input element tell the WebView thread
+ // to set the cursor to that control.
+ if (isContentInputElement(nextAnchorNode)) {
+ IntRect bounds = nextAnchorNode->getRect();
+ selectAt(bounds.center().x(), bounds.center().y());
+ }
+ Node* textNode = 0;
+ // Treat the text content of links as any other text but
+ // for the rest input elements select the control itself.
+ if (nextAnchorNode->hasTagName(WebCore::HTMLNames::aTag))
+ textNode = traverseNextContentTextNode(nextAnchorNode,
+ nextAnchorNode, direction);
+ // We prefer to select the text content of the link if such,
+ // otherwise just select the element itself.
+ if (textNode) {
+ nextAnchorNode = textNode;
+ } else {
+ if (direction == DIRECTION_FORWARD) {
+ selection->setBaseAndExtent(nextAnchorNode,
+ caretMinOffset(nextAnchorNode), nextAnchorNode,
+ caretMaxOffset(nextAnchorNode), ec);
+ } else {
+ selection->setBaseAndExtent(nextAnchorNode,
+ caretMaxOffset(nextAnchorNode), nextAnchorNode,
+ caretMinOffset(nextAnchorNode), ec);
+ }
+ if (!ec)
+ markup = formatMarkup(selection);
+ // make sure the selection is visible
+ scrollNodeIntoView(selection->frame(), nextAnchorNode);
+ return;
+ }
+ }
+ if (direction == DIRECTION_FORWARD)
+ selection->setPosition(nextAnchorNode,
+ caretMinOffset(nextAnchorNode), ec);
+ else
+ selection->setPosition(nextAnchorNode,
+ caretMaxOffset(nextAnchorNode), ec);
+}
+
+bool WebViewCore::isContentInputElement(Node* node)
+{
+ return (isVisible(node)
+ && (node->hasTagName(WebCore::HTMLNames::selectTag)
+ || node->hasTagName(WebCore::HTMLNames::aTag)
+ || node->hasTagName(WebCore::HTMLNames::inputTag)
+ || node->hasTagName(WebCore::HTMLNames::buttonTag)));
+}
+
+bool WebViewCore::isContentTextNode(Node* node)
+{
+ if (!node || !node->isTextNode())
+ return false;
+ Text* textNode = static_cast<Text*>(node);
+ return (isVisible(textNode) && textNode->length() > 0
+ && !textNode->containsOnlyWhitespace());
+}
+
+Text* WebViewCore::traverseNextContentTextNode(Node* fromNode, Node* toNode, int direction)
+{
+ Node* currentNode = fromNode;
+ do {
+ if (direction == DIRECTION_FORWARD)
+ currentNode = currentNode->traverseNextNode(toNode);
+ else
+ currentNode = currentNode->traversePreviousNodePostOrder(toNode);
+ } while (currentNode && !isContentTextNode(currentNode));
+ return static_cast<Text*>(currentNode);
+}
+
+Node* WebViewCore::getIntermediaryInputElement(Node* fromNode, Node* toNode, int direction)
+{
+ if (fromNode == toNode)
+ return 0;
+ if (direction == DIRECTION_FORWARD) {
+ Node* currentNode = fromNode;
+ while (currentNode && currentNode != toNode) {
+ if (isContentInputElement(currentNode))
+ return currentNode;
+ currentNode = currentNode->traverseNextNodePostOrder();
+ }
+ currentNode = fromNode;
+ while (currentNode && currentNode != toNode) {
+ if (isContentInputElement(currentNode))
+ return currentNode;
+ currentNode = currentNode->traverseNextNode();
+ }
+ } else {
+ Node* currentNode = fromNode->traversePreviousNode();
+ while (currentNode && currentNode != toNode) {
+ if (isContentInputElement(currentNode))
+ return currentNode;
+ currentNode = currentNode->traversePreviousNode();
+ }
+ currentNode = fromNode->traversePreviousNodePostOrder();
+ while (currentNode && currentNode != toNode) {
+ if (isContentInputElement(currentNode))
+ return currentNode;
+ currentNode = currentNode->traversePreviousNodePostOrder();
+ }
+ }
+ return 0;
+}
+
+bool WebViewCore::isDescendantOf(Node* parent, Node* node)
+{
+ Node* currentNode = node;
+ while (currentNode) {
+ if (currentNode == parent) {
+ return true;
+ }
+ currentNode = currentNode->parentNode();
+ }
+ return false;
+}
+
+String WebViewCore::modifySelectionDomNavigationAxis(DOMSelection* selection, int direction, int axis)
+{
+ HTMLElement* body = m_mainFrame->document()->body();
+ if (!m_currentNodeDomNavigationAxis && selection->focusNode()) {
+ m_currentNodeDomNavigationAxis = selection->focusNode();
+ selection->empty();
+ if (m_currentNodeDomNavigationAxis->isTextNode())
+ m_currentNodeDomNavigationAxis =
+ m_currentNodeDomNavigationAxis->parentNode();
+ }
+ if (!m_currentNodeDomNavigationAxis)
+ m_currentNodeDomNavigationAxis = currentFocus();
+ if (!m_currentNodeDomNavigationAxis
+ || !CacheBuilder::validNode(m_mainFrame, m_mainFrame,
+ m_currentNodeDomNavigationAxis))
+ m_currentNodeDomNavigationAxis = body;
+ Node* currentNode = m_currentNodeDomNavigationAxis;
+ if (axis == AXIS_HEADING) {
+ if (currentNode == body && direction == DIRECTION_BACKWARD)
+ currentNode = currentNode->lastDescendant();
+ do {
+ if (direction == DIRECTION_FORWARD)
+ currentNode = currentNode->traverseNextNode(body);
+ else
+ currentNode = currentNode->traversePreviousNode(body);
+ } while (currentNode && (currentNode->isTextNode()
+ || !isVisible(currentNode) || !isHeading(currentNode)));
+ } else if (axis == AXIS_PARENT_FIRST_CHILD) {
+ if (direction == DIRECTION_FORWARD) {
+ currentNode = currentNode->firstChild();
+ while (currentNode && (currentNode->isTextNode()
+ || !isVisible(currentNode)))
+ currentNode = currentNode->nextSibling();
+ } else {
+ do {
+ if (currentNode == body)
+ return String();
+ currentNode = currentNode->parentNode();
+ } while (currentNode && (currentNode->isTextNode()
+ || !isVisible(currentNode)));
+ }
+ } else if (axis == AXIS_SIBLING) {
+ do {
+ if (direction == DIRECTION_FORWARD)
+ currentNode = currentNode->nextSibling();
+ else {
+ if (currentNode == body)
+ return String();
+ currentNode = currentNode->previousSibling();
+ }
+ } while (currentNode && (currentNode->isTextNode()
+ || !isVisible(currentNode)));
+ } else if (axis == AXIS_DOCUMENT) {
+ currentNode = body;
+ if (direction == DIRECTION_FORWARD)
+ currentNode = currentNode->lastDescendant();
+ } else {
+ LOGE("Invalid axis: %d", axis);
+ return String();
+ }
+ if (currentNode) {
+ m_currentNodeDomNavigationAxis = currentNode;
+ scrollNodeIntoView(m_mainFrame, currentNode);
+ String selectionString = createMarkup(currentNode);
+ LOGV("Selection markup: %s", selectionString.utf8().data());
+ return selectionString;
+ }
+ return String();
+}
+
+bool WebViewCore::isHeading(Node* node)
+{
+ if (node->hasTagName(WebCore::HTMLNames::h1Tag)
+ || node->hasTagName(WebCore::HTMLNames::h2Tag)
+ || node->hasTagName(WebCore::HTMLNames::h3Tag)
+ || node->hasTagName(WebCore::HTMLNames::h4Tag)
+ || node->hasTagName(WebCore::HTMLNames::h5Tag)
+ || node->hasTagName(WebCore::HTMLNames::h6Tag)) {
+ return true;
+ }
+
+ if (node->isElementNode()) {
+ Element* element = static_cast<Element*>(node);
+ String roleAttribute =
+ element->getAttribute(WebCore::HTMLNames::roleAttr).string();
+ if (equalIgnoringCase(roleAttribute, "heading"))
+ return true;
+ }
+
+ return false;
+}
+
+bool WebViewCore::isVisible(Node* node)
+{
+ // start off an element
+ Element* element = 0;
+ if (node->isElementNode())
+ element = static_cast<Element*>(node);
+ else
+ element = node->parentElement();
+ // check renderer
+ if (!element->renderer()) {
+ return false;
+ }
+ // check size
+ if (element->offsetHeight() == 0 || element->offsetWidth() == 0) {
+ return false;
+ }
+ // check style
+ Node* body = m_mainFrame->document()->body();
+ Node* currentNode = element;
+ while (currentNode && currentNode != body) {
+ RenderStyle* style = currentNode->computedStyle();
+ if (style &&
+ (style->display() == NONE || style->visibility() == HIDDEN)) {
+ return false;
+ }
+ currentNode = currentNode->parentNode();
+ }
+ return true;
+}
+
+String WebViewCore::formatMarkup(DOMSelection* selection)
+{
+ ExceptionCode ec = 0;
+ String markup = String();
+ PassRefPtr<Range> wholeRange = selection->getRangeAt(0, ec);
+ if (ec)
+ return String();
+ if (!wholeRange->startContainer() || !wholeRange->startContainer())
+ return String();
+ // Since formatted markup contains invisible nodes it
+ // is created from the concatenation of the visible fragments.
+ Node* firstNode = wholeRange->firstNode();
+ Node* pastLastNode = wholeRange->pastLastNode();
+ Node* currentNode = firstNode;
+ PassRefPtr<Range> currentRange;
+
+ while (currentNode != pastLastNode) {
+ Node* nextNode = currentNode->traverseNextNode();
+ if (!isVisible(currentNode)) {
+ if (currentRange) {
+ markup = markup + currentRange->toHTML().utf8().data();
+ currentRange = 0;
+ }
+ } else {
+ if (!currentRange) {
+ currentRange = selection->frame()->document()->createRange();
+ if (ec)
+ break;
+ if (currentNode == firstNode) {
+ currentRange->setStart(wholeRange->startContainer(),
+ wholeRange->startOffset(), ec);
+ if (ec)
+ break;
+ } else {
+ currentRange->setStart(currentNode->parentNode(),
+ currentNode->nodeIndex(), ec);
+ if (ec)
+ break;
+ }
+ }
+ if (nextNode == pastLastNode) {
+ currentRange->setEnd(wholeRange->endContainer(),
+ wholeRange->endOffset(), ec);
+ if (ec)
+ break;
+ markup = markup + currentRange->toHTML().utf8().data();
+ } else {
+ if (currentNode->offsetInCharacters())
+ currentRange->setEnd(currentNode,
+ currentNode->maxCharacterOffset(), ec);
+ else
+ currentRange->setEnd(currentNode->parentNode(),
+ currentNode->nodeIndex() + 1, ec);
+ if (ec)
+ break;
+ }
+ }
+ currentNode = nextNode;
+ }
+ return markup.stripWhiteSpace();
+}
+
+void WebViewCore::selectAt(int x, int y)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_selectAt,
+ x, y);
+ checkException(env);
+}
+
+void WebViewCore::deleteSelection(int start, int end, int textGeneration)
+{
+ setSelection(start, end);
+ if (start == end)
+ return;
+ WebCore::Node* focus = currentFocus();
+ if (!focus)
+ return;
+ // Prevent our editor client from passing a message to change the
+ // selection.
+ EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
+ m_mainFrame->editor()->client());
+ client->setUiGeneratedSelectionChange(true);
+ PlatformKeyboardEvent down(AKEYCODE_DEL, 0, 0, true, false, false, false);
+ PlatformKeyboardEvent up(AKEYCODE_DEL, 0, 0, false, false, false, false);
+ key(down);
+ key(up);
+ client->setUiGeneratedSelectionChange(false);
+ m_textGeneration = textGeneration;
+ m_shouldPaintCaret = true;
+}
+
+void WebViewCore::replaceTextfieldText(int oldStart,
+ int oldEnd, const WTF::String& replace, int start, int end,
+ int textGeneration)
+{
+ WebCore::Node* focus = currentFocus();
+ if (!focus)
+ return;
+ setSelection(oldStart, oldEnd);
+ // Prevent our editor client from passing a message to change the
+ // selection.
+ EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
+ m_mainFrame->editor()->client());
+ client->setUiGeneratedSelectionChange(true);
+ WebCore::TypingCommand::insertText(focus->document(), replace,
+ false);
+ client->setUiGeneratedSelectionChange(false);
+ // setSelection calls revealSelection, so there is no need to do it here.
+ setSelection(start, end);
+ m_textGeneration = textGeneration;
+ m_shouldPaintCaret = true;
+}
+
+void WebViewCore::passToJs(int generation, const WTF::String& current,
+ const PlatformKeyboardEvent& event)
+{
+ WebCore::Node* focus = currentFocus();
+ if (!focus) {
+ DBG_NAV_LOG("!focus");
+ clearTextEntry();
+ return;
+ }
+ WebCore::RenderObject* renderer = focus->renderer();
+ if (!renderer || (!renderer->isTextField() && !renderer->isTextArea())) {
+ DBG_NAV_LOGD("renderer==%p || not text", renderer);
+ clearTextEntry();
+ return;
+ }
+ // Block text field updates during a key press.
+ m_blockTextfieldUpdates = true;
+ // Also prevent our editor client from passing a message to change the
+ // selection.
+ EditorClientAndroid* client = static_cast<EditorClientAndroid*>(
+ m_mainFrame->editor()->client());
+ client->setUiGeneratedSelectionChange(true);
+ key(event);
+ client->setUiGeneratedSelectionChange(false);
+ m_blockTextfieldUpdates = false;
+ m_textGeneration = generation;
+ WebCore::RenderTextControl* renderText =
+ static_cast<WebCore::RenderTextControl*>(renderer);
+ WTF::String test = renderText->text();
+ if (test != current) {
+ // If the text changed during the key event, update the UI text field.
+ updateTextfield(focus, false, test);
+ } else {
+ DBG_NAV_LOG("test == current");
+ }
+ // Now that the selection has settled down, send it.
+ updateTextSelection();
+ m_shouldPaintCaret = true;
+}
+
+void WebViewCore::scrollFocusedTextInput(float xPercent, int y)
+{
+ WebCore::Node* focus = currentFocus();
+ if (!focus) {
+ DBG_NAV_LOG("!focus");
+ clearTextEntry();
+ return;
+ }
+ WebCore::RenderObject* renderer = focus->renderer();
+ if (!renderer || (!renderer->isTextField() && !renderer->isTextArea())) {
+ DBG_NAV_LOGD("renderer==%p || not text", renderer);
+ clearTextEntry();
+ return;
+ }
+ WebCore::RenderTextControl* renderText =
+ static_cast<WebCore::RenderTextControl*>(renderer);
+ int x = (int) (xPercent * (renderText->scrollWidth() -
+ renderText->clientWidth()));
+ DBG_NAV_LOGD("x=%d y=%d xPercent=%g scrollW=%d clientW=%d", x, y,
+ xPercent, renderText->scrollWidth(), renderText->clientWidth());
+ renderText->setScrollLeft(x);
+ renderText->setScrollTop(y);
+}
+
+void WebViewCore::setFocusControllerActive(bool active)
+{
+ m_mainFrame->page()->focusController()->setActive(active);
+}
+
+void WebViewCore::saveDocumentState(WebCore::Frame* frame)
+{
+ if (!CacheBuilder::validNode(m_mainFrame, frame, 0))
+ frame = m_mainFrame;
+ WebCore::HistoryItem *item = frame->loader()->history()->currentItem();
+
+ // item can be null when there is no offical URL for the current page. This happens
+ // when the content is loaded using with WebCoreFrameBridge::LoadData() and there
+ // is no failing URL (common case is when content is loaded using data: scheme)
+ if (item) {
+ item->setDocumentState(frame->document()->formElementsState());
+ }
+}
+
+// Create an array of java Strings.
+static jobjectArray makeLabelArray(JNIEnv* env, const uint16_t** labels, size_t count)
+{
+ jclass stringClass = env->FindClass("java/lang/String");
+ LOG_ASSERT(stringClass, "Could not find java/lang/String");
+ jobjectArray array = env->NewObjectArray(count, stringClass, 0);
+ LOG_ASSERT(array, "Could not create new string array");
+
+ for (size_t i = 0; i < count; i++) {
+ jobject newString = env->NewString(&labels[i][1], labels[i][0]);
+ env->SetObjectArrayElement(array, i, newString);
+ env->DeleteLocalRef(newString);
+ checkException(env);
+ }
+ env->DeleteLocalRef(stringClass);
+ return array;
+}
+
+void WebViewCore::openFileChooser(PassRefPtr<WebCore::FileChooser> chooser) {
+ if (!chooser)
+ return;
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+
+ WTF::String acceptType = chooser->acceptTypes();
+ jstring jAcceptType = wtfStringToJstring(env, acceptType, true);
+ jstring jName = (jstring) env->CallObjectMethod(
+ m_javaGlue->object(env).get(), m_javaGlue->m_openFileChooser, jAcceptType);
+ checkException(env);
+ env->DeleteLocalRef(jAcceptType);
+
+ const UChar* string = static_cast<const UChar*>(env->GetStringChars(jName, NULL));
+
+ if (!string)
+ return;
+
+ WTF::String webcoreString = jstringToWtfString(env, jName);
+ env->ReleaseStringChars(jName, string);
+
+ if (webcoreString.length())
+ chooser->chooseFile(webcoreString);
+}
+
+void WebViewCore::listBoxRequest(WebCoreReply* reply, const uint16_t** labels, size_t count, const int enabled[], size_t enabledCount,
+ bool multiple, const int selected[], size_t selectedCountOrSelection)
+{
+ // If m_popupReply is not null, then we already have a list showing.
+ if (m_popupReply != 0)
+ return;
+
+ LOG_ASSERT(m_javaGlue->m_obj, "No java widget associated with this view!");
+
+ // Create an array of java Strings for the drop down.
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jobjectArray labelArray = makeLabelArray(env, labels, count);
+
+ // Create an array determining whether each item is enabled.
+ jintArray enabledArray = env->NewIntArray(enabledCount);
+ checkException(env);
+ jint* ptrArray = env->GetIntArrayElements(enabledArray, 0);
+ checkException(env);
+ for (size_t i = 0; i < enabledCount; i++) {
+ ptrArray[i] = enabled[i];
+ }
+ env->ReleaseIntArrayElements(enabledArray, ptrArray, 0);
+ checkException(env);
+
+ if (multiple) {
+ // Pass up an array representing which items are selected.
+ jintArray selectedArray = env->NewIntArray(selectedCountOrSelection);
+ checkException(env);
+ jint* selArray = env->GetIntArrayElements(selectedArray, 0);
+ checkException(env);
+ for (size_t i = 0; i < selectedCountOrSelection; i++) {
+ selArray[i] = selected[i];
+ }
+ env->ReleaseIntArrayElements(selectedArray, selArray, 0);
+
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_requestListBox, labelArray, enabledArray,
+ selectedArray);
+ env->DeleteLocalRef(selectedArray);
+ } else {
+ // Pass up the single selection.
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_requestSingleListBox, labelArray, enabledArray,
+ selectedCountOrSelection);
+ }
+
+ env->DeleteLocalRef(labelArray);
+ env->DeleteLocalRef(enabledArray);
+ checkException(env);
+
+ Retain(reply);
+ m_popupReply = reply;
+}
+
+bool WebViewCore::key(const PlatformKeyboardEvent& event)
+{
+ WebCore::EventHandler* eventHandler;
+ WebCore::Node* focusNode = currentFocus();
+ DBG_NAV_LOGD("keyCode=%s unichar=%d focusNode=%p",
+ event.keyIdentifier().utf8().data(), event.unichar(), focusNode);
+ if (focusNode) {
+ WebCore::Frame* frame = focusNode->document()->frame();
+ WebFrame* webFrame = WebFrame::getWebFrame(frame);
+ eventHandler = frame->eventHandler();
+ VisibleSelection old = frame->selection()->selection();
+ bool handled = eventHandler->keyEvent(event);
+ if (isContentEditable(focusNode)) {
+ // keyEvent will return true even if the contentEditable did not
+ // change its selection. In the case that it does not, we want to
+ // return false so that the key will be sent back to our navigation
+ // system.
+ handled |= frame->selection()->selection() != old;
+ }
+ return handled;
+ } else {
+ eventHandler = m_mainFrame->eventHandler();
+ }
+ return eventHandler->keyEvent(event);
+}
+
+// For when the user clicks the trackball, presses dpad center, or types into an
+// unfocused textfield. In the latter case, 'fake' will be true
+void WebViewCore::click(WebCore::Frame* frame, WebCore::Node* node, bool fake) {
+ if (!node) {
+ WebCore::IntPoint pt = m_mousePos;
+ pt.move(m_scrollOffsetX, m_scrollOffsetY);
+ WebCore::HitTestResult hitTestResult = m_mainFrame->eventHandler()->
+ hitTestResultAtPoint(pt, false);
+ node = hitTestResult.innerNode();
+ frame = node->document()->frame();
+ DBG_NAV_LOGD("m_mousePos=(%d,%d) m_scrollOffset=(%d,%d) pt=(%d,%d)"
+ " node=%p", m_mousePos.x(), m_mousePos.y(),
+ m_scrollOffsetX, m_scrollOffsetY, pt.x(), pt.y(), node);
+ }
+ if (node) {
+ EditorClientAndroid* client
+ = static_cast<EditorClientAndroid*>(
+ m_mainFrame->editor()->client());
+ client->setShouldChangeSelectedRange(false);
+ handleMouseClick(frame, node, fake);
+ client->setShouldChangeSelectedRange(true);
+ }
+}
+
+#if USE(ACCELERATED_COMPOSITING)
+GraphicsLayerAndroid* WebViewCore::graphicsRootLayer() const
+{
+ RenderView* contentRenderer = m_mainFrame->contentRenderer();
+ if (!contentRenderer)
+ return 0;
+ return static_cast<GraphicsLayerAndroid*>(
+ contentRenderer->compositor()->rootPlatformLayer());
+}
+#endif
+
+bool WebViewCore::handleTouchEvent(int action, Vector<int>& ids, Vector<IntPoint>& points, int actionIndex, int metaState)
+{
+ bool preventDefault = false;
+
+#if USE(ACCELERATED_COMPOSITING)
+ GraphicsLayerAndroid* rootLayer = graphicsRootLayer();
+ if (rootLayer)
+ rootLayer->pauseDisplay(true);
+#endif
+
+#if ENABLE(TOUCH_EVENTS) // Android
+ #define MOTION_EVENT_ACTION_POINTER_DOWN 5
+ #define MOTION_EVENT_ACTION_POINTER_UP 6
+
+ WebCore::TouchEventType type = WebCore::TouchStart;
+ WebCore::PlatformTouchPoint::State defaultTouchState;
+ Vector<WebCore::PlatformTouchPoint::State> touchStates(points.size());
+
+ switch (action) {
+ case 0: // MotionEvent.ACTION_DOWN
+ type = WebCore::TouchStart;
+ defaultTouchState = WebCore::PlatformTouchPoint::TouchPressed;
+ break;
+ case 1: // MotionEvent.ACTION_UP
+ type = WebCore::TouchEnd;
+ defaultTouchState = WebCore::PlatformTouchPoint::TouchReleased;
+ break;
+ case 2: // MotionEvent.ACTION_MOVE
+ type = WebCore::TouchMove;
+ defaultTouchState = WebCore::PlatformTouchPoint::TouchMoved;
+ break;
+ case 3: // MotionEvent.ACTION_CANCEL
+ type = WebCore::TouchCancel;
+ defaultTouchState = WebCore::PlatformTouchPoint::TouchCancelled;
+ break;
+ case 5: // MotionEvent.ACTION_POINTER_DOWN
+ type = WebCore::TouchStart;
+ defaultTouchState = WebCore::PlatformTouchPoint::TouchStationary;
+ break;
+ case 6: // MotionEvent.ACTION_POINTER_UP
+ type = WebCore::TouchEnd;
+ defaultTouchState = WebCore::PlatformTouchPoint::TouchStationary;
+ break;
+ case 0x100: // WebViewCore.ACTION_LONGPRESS
+ type = WebCore::TouchLongPress;
+ defaultTouchState = WebCore::PlatformTouchPoint::TouchPressed;
+ break;
+ case 0x200: // WebViewCore.ACTION_DOUBLETAP
+ type = WebCore::TouchDoubleTap;
+ defaultTouchState = WebCore::PlatformTouchPoint::TouchPressed;
+ break;
+ default:
+ // We do not support other kinds of touch event inside WebCore
+ // at the moment.
+ LOGW("Java passed a touch event type that we do not support in WebCore: %d", action);
+ return 0;
+ }
+
+ for (int c = 0; c < static_cast<int>(points.size()); c++) {
+ points[c].setX(points[c].x() - m_scrollOffsetX);
+ points[c].setY(points[c].y() - m_scrollOffsetY);
+
+ // Setting the touch state for each point.
+ // Note: actionIndex will be 0 for all actions that are not ACTION_POINTER_DOWN/UP.
+ if (action == MOTION_EVENT_ACTION_POINTER_DOWN && c == actionIndex) {
+ touchStates[c] = WebCore::PlatformTouchPoint::TouchPressed;
+ } else if (action == MOTION_EVENT_ACTION_POINTER_UP && c == actionIndex) {
+ touchStates[c] = WebCore::PlatformTouchPoint::TouchReleased;
+ } else {
+ touchStates[c] = defaultTouchState;
+ };
+ }
+
+ WebCore::PlatformTouchEvent te(ids, points, type, touchStates, metaState);
+ preventDefault = m_mainFrame->eventHandler()->handleTouchEvent(te);
+#endif
+
+#if USE(ACCELERATED_COMPOSITING)
+ if (rootLayer)
+ rootLayer->pauseDisplay(false);
+#endif
+ return preventDefault;
+}
+
+void WebViewCore::touchUp(int touchGeneration,
+ WebCore::Frame* frame, WebCore::Node* node, int x, int y)
+{
+ if (touchGeneration == 0) {
+ // m_mousePos should be set in getTouchHighlightRects()
+ WebCore::HitTestResult hitTestResult = m_mainFrame->eventHandler()->hitTestResultAtPoint(m_mousePos, false);
+ node = hitTestResult.innerNode();
+ if (node)
+ frame = node->document()->frame();
+ else
+ frame = 0;
+ DBG_NAV_LOGD("touch up on (%d, %d), scrollOffset is (%d, %d), node:%p, frame:%p", m_mousePos.x() + m_scrollOffsetX, m_mousePos.y() + m_scrollOffsetY, m_scrollOffsetX, m_scrollOffsetY, node, frame);
+ } else {
+ if (m_touchGeneration > touchGeneration) {
+ DBG_NAV_LOGD("m_touchGeneration=%d > touchGeneration=%d"
+ " x=%d y=%d", m_touchGeneration, touchGeneration, x, y);
+ return; // short circuit if a newer touch has been generated
+ }
+ // This moves m_mousePos to the correct place, and handleMouseClick uses
+ // m_mousePos to determine where the click happens.
+ moveMouse(frame, x, y);
+ m_lastGeneration = touchGeneration;
+ }
+ if (frame && CacheBuilder::validNode(m_mainFrame, frame, 0)) {
+ frame->loader()->resetMultipleFormSubmissionProtection();
+ }
+ DBG_NAV_LOGD("touchGeneration=%d handleMouseClick frame=%p node=%p"
+ " x=%d y=%d", touchGeneration, frame, node, x, y);
+ handleMouseClick(frame, node, false);
+}
+
+// Check for the "x-webkit-soft-keyboard" attribute. If it is there and
+// set to hidden, do not show the soft keyboard. Node passed as a parameter
+// must not be null.
+static bool shouldSuppressKeyboard(const WebCore::Node* node) {
+ LOG_ASSERT(node, "node passed to shouldSuppressKeyboard cannot be null");
+ const NamedNodeMap* attributes = node->attributes();
+ if (!attributes) return false;
+ size_t length = attributes->length();
+ for (size_t i = 0; i < length; i++) {
+ const Attribute* a = attributes->attributeItem(i);
+ if (a->localName() == "x-webkit-soft-keyboard" && a->value() == "hidden")
+ return true;
+ }
+ return false;
+}
+
+// Common code for both clicking with the trackball and touchUp
+// Also used when typing into a non-focused textfield to give the textfield focus,
+// in which case, 'fake' is set to true
+bool WebViewCore::handleMouseClick(WebCore::Frame* framePtr, WebCore::Node* nodePtr, bool fake)
+{
+ bool valid = !framePtr || CacheBuilder::validNode(m_mainFrame, framePtr, nodePtr);
+ WebFrame* webFrame = WebFrame::getWebFrame(m_mainFrame);
+ if (valid && nodePtr) {
+ // Need to special case area tags because an image map could have an area element in the middle
+ // so when attempting to get the default, the point chosen would be follow the wrong link.
+ if (nodePtr->hasTagName(WebCore::HTMLNames::areaTag)) {
+ webFrame->setUserInitiatedAction(true);
+ nodePtr->dispatchSimulatedClick(0, true, true);
+ webFrame->setUserInitiatedAction(false);
+ DBG_NAV_LOG("area");
+ return true;
+ }
+ }
+ if (!valid || !framePtr)
+ framePtr = m_mainFrame;
+ webFrame->setUserInitiatedAction(true);
+ WebCore::PlatformMouseEvent mouseDown(m_mousePos, m_mousePos, WebCore::LeftButton,
+ WebCore::MouseEventPressed, 1, false, false, false, false,
+ WTF::currentTime());
+ // ignore the return from as it will return true if the hit point can trigger selection change
+ framePtr->eventHandler()->handleMousePressEvent(mouseDown);
+ WebCore::PlatformMouseEvent mouseUp(m_mousePos, m_mousePos, WebCore::LeftButton,
+ WebCore::MouseEventReleased, 1, false, false, false, false,
+ WTF::currentTime());
+ bool handled = framePtr->eventHandler()->handleMouseReleaseEvent(mouseUp);
+ webFrame->setUserInitiatedAction(false);
+
+ // If the user clicked on a textfield, make the focusController active
+ // so we show the blinking cursor.
+ WebCore::Node* focusNode = currentFocus();
+ DBG_NAV_LOGD("m_mousePos={%d,%d} focusNode=%p handled=%s", m_mousePos.x(),
+ m_mousePos.y(), focusNode, handled ? "true" : "false");
+ if (focusNode) {
+ WebCore::RenderObject* renderer = focusNode->renderer();
+ if (renderer && (renderer->isTextField() || renderer->isTextArea())) {
+ bool ime = !shouldSuppressKeyboard(focusNode)
+ && !(static_cast<WebCore::HTMLInputElement*>(focusNode))->readOnly();
+ if (ime) {
+#if ENABLE(WEB_AUTOFILL)
+ if (renderer->isTextField()) {
+ EditorClientAndroid* editorC = static_cast<EditorClientAndroid*>(framePtr->page()->editorClient());
+ WebAutoFill* autoFill = editorC->getAutoFill();
+ autoFill->formFieldFocused(static_cast<HTMLFormControlElement*>(focusNode));
+ }
+#endif
+ if (!fake) {
+ RenderTextControl* rtc
+ = static_cast<RenderTextControl*> (renderer);
+ requestKeyboardWithSelection(focusNode, rtc->selectionStart(),
+ rtc->selectionEnd());
+ }
+ } else if (!fake) {
+ requestKeyboard(false);
+ }
+ } else if (!fake){
+ // If the selection is contentEditable, show the keyboard so the
+ // user can type. Otherwise hide the keyboard because no text
+ // input is needed.
+ if (isContentEditable(focusNode)) {
+ requestKeyboard(true);
+ } else if (!nodeIsPlugin(focusNode)) {
+ clearTextEntry();
+ }
+ }
+ } else if (!fake) {
+ // There is no focusNode, so the keyboard is not needed.
+ clearTextEntry();
+ }
+ return handled;
+}
+
+void WebViewCore::popupReply(int index)
+{
+ if (m_popupReply) {
+ m_popupReply->replyInt(index);
+ Release(m_popupReply);
+ m_popupReply = 0;
+ }
+}
+
+void WebViewCore::popupReply(const int* array, int count)
+{
+ if (m_popupReply) {
+ m_popupReply->replyIntArray(array, count);
+ Release(m_popupReply);
+ m_popupReply = 0;
+ }
+}
+
+void WebViewCore::formDidBlur(const WebCore::Node* node)
+{
+ // If the blur is on a text input, keep track of the node so we can
+ // hide the soft keyboard when the new focus is set, if it is not a
+ // text input.
+ if (isTextInput(node))
+ m_blurringNodePointer = reinterpret_cast<int>(node);
+}
+
+void WebViewCore::focusNodeChanged(const WebCore::Node* newFocus)
+{
+ if (isTextInput(newFocus))
+ m_shouldPaintCaret = true;
+ else if (m_blurringNodePointer) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_formDidBlur, m_blurringNodePointer);
+ checkException(env);
+ m_blurringNodePointer = 0;
+ }
+}
+
+void WebViewCore::addMessageToConsole(const WTF::String& message, unsigned int lineNumber, const WTF::String& sourceID, int msgLevel) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jMessageStr = wtfStringToJstring(env, message);
+ jstring jSourceIDStr = wtfStringToJstring(env, sourceID);
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_addMessageToConsole, jMessageStr, lineNumber,
+ jSourceIDStr, msgLevel);
+ env->DeleteLocalRef(jMessageStr);
+ env->DeleteLocalRef(jSourceIDStr);
+ checkException(env);
+}
+
+void WebViewCore::jsAlert(const WTF::String& url, const WTF::String& text)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jInputStr = wtfStringToJstring(env, text);
+ jstring jUrlStr = wtfStringToJstring(env, url);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsAlert, jUrlStr, jInputStr);
+ env->DeleteLocalRef(jInputStr);
+ env->DeleteLocalRef(jUrlStr);
+ checkException(env);
+}
+
+void WebViewCore::exceededDatabaseQuota(const WTF::String& url, const WTF::String& databaseIdentifier, const unsigned long long currentQuota, unsigned long long estimatedSize)
+{
+#if ENABLE(DATABASE)
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jDatabaseIdentifierStr = wtfStringToJstring(env, databaseIdentifier);
+ jstring jUrlStr = wtfStringToJstring(env, url);
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_exceededDatabaseQuota, jUrlStr,
+ jDatabaseIdentifierStr, currentQuota, estimatedSize);
+ env->DeleteLocalRef(jDatabaseIdentifierStr);
+ env->DeleteLocalRef(jUrlStr);
+ checkException(env);
+#endif
+}
+
+void WebViewCore::reachedMaxAppCacheSize(const unsigned long long spaceNeeded)
+{
+#if ENABLE(OFFLINE_WEB_APPLICATIONS)
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_reachedMaxAppCacheSize, spaceNeeded);
+ checkException(env);
+#endif
+}
+
+void WebViewCore::populateVisitedLinks(WebCore::PageGroup* group)
+{
+ m_groupForVisitedLinks = group;
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_populateVisitedLinks);
+ checkException(env);
+}
+
+void WebViewCore::geolocationPermissionsShowPrompt(const WTF::String& origin)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring originString = wtfStringToJstring(env, origin);
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_geolocationPermissionsShowPrompt,
+ originString);
+ env->DeleteLocalRef(originString);
+ checkException(env);
+}
+
+void WebViewCore::geolocationPermissionsHidePrompt()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_geolocationPermissionsHidePrompt);
+ checkException(env);
+}
+
+jobject WebViewCore::getDeviceMotionService()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jobject object = env->CallObjectMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_getDeviceMotionService);
+ checkException(env);
+ return object;
+}
+
+jobject WebViewCore::getDeviceOrientationService()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jobject object = env->CallObjectMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_getDeviceOrientationService);
+ checkException(env);
+ return object;
+}
+
+bool WebViewCore::jsConfirm(const WTF::String& url, const WTF::String& text)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jInputStr = wtfStringToJstring(env, text);
+ jstring jUrlStr = wtfStringToJstring(env, url);
+ jboolean result = env->CallBooleanMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsConfirm, jUrlStr, jInputStr);
+ env->DeleteLocalRef(jInputStr);
+ env->DeleteLocalRef(jUrlStr);
+ checkException(env);
+ return result;
+}
+
+bool WebViewCore::jsPrompt(const WTF::String& url, const WTF::String& text, const WTF::String& defaultValue, WTF::String& result)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jUrlStr = wtfStringToJstring(env, url);
+ jstring jInputStr = wtfStringToJstring(env, text);
+ jstring jDefaultStr = wtfStringToJstring(env, defaultValue);
+ jstring returnVal = static_cast<jstring>(env->CallObjectMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsPrompt, jUrlStr, jInputStr, jDefaultStr));
+ env->DeleteLocalRef(jUrlStr);
+ env->DeleteLocalRef(jInputStr);
+ env->DeleteLocalRef(jDefaultStr);
+ checkException(env);
+
+ // If returnVal is null, it means that the user cancelled the dialog.
+ if (!returnVal)
+ return false;
+
+ result = jstringToWtfString(env, returnVal);
+ env->DeleteLocalRef(returnVal);
+ return true;
+}
+
+bool WebViewCore::jsUnload(const WTF::String& url, const WTF::String& message)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jInputStr = wtfStringToJstring(env, message);
+ jstring jUrlStr = wtfStringToJstring(env, url);
+ jboolean result = env->CallBooleanMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsUnload, jUrlStr, jInputStr);
+ env->DeleteLocalRef(jInputStr);
+ env->DeleteLocalRef(jUrlStr);
+ checkException(env);
+ return result;
+}
+
+bool WebViewCore::jsInterrupt()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jboolean result = env->CallBooleanMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsInterrupt);
+ checkException(env);
+ return result;
+}
+
+AutoJObject
+WebViewCore::getJavaObject()
+{
+ return m_javaGlue->object(JSC::Bindings::getJNIEnv());
+}
+
+jobject
+WebViewCore::getWebViewJavaObject()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ return env->GetObjectField(m_javaGlue->object(env).get(), gWebViewCoreFields.m_webView);
+}
+
+void WebViewCore::updateTextSelection() {
+ WebCore::Node* focusNode = currentFocus();
+ if (!focusNode)
+ return;
+ RenderObject* renderer = focusNode->renderer();
+ if (!renderer || (!renderer->isTextArea() && !renderer->isTextField()))
+ return;
+ RenderTextControl* rtc = static_cast<RenderTextControl*>(renderer);
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_updateTextSelection, reinterpret_cast<int>(focusNode),
+ rtc->selectionStart(), rtc->selectionEnd(), m_textGeneration);
+ checkException(env);
+}
+
+void WebViewCore::updateTextfield(WebCore::Node* ptr, bool changeToPassword,
+ const WTF::String& text)
+{
+ if (m_blockTextfieldUpdates)
+ return;
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (changeToPassword) {
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_updateTextfield,
+ (int) ptr, true, 0, m_textGeneration);
+ checkException(env);
+ return;
+ }
+ jstring string = wtfStringToJstring(env, text);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_updateTextfield,
+ (int) ptr, false, string, m_textGeneration);
+ env->DeleteLocalRef(string);
+ checkException(env);
+}
+
+void WebViewCore::clearTextEntry()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_clearTextEntry);
+}
+
+void WebViewCore::setBackgroundColor(SkColor c)
+{
+ WebCore::FrameView* view = m_mainFrame->view();
+ if (!view)
+ return;
+
+ // need (int) cast to find the right constructor
+ WebCore::Color bcolor((int)SkColorGetR(c), (int)SkColorGetG(c),
+ (int)SkColorGetB(c), (int)SkColorGetA(c));
+ view->setBaseBackgroundColor(bcolor);
+
+ // Background color of 0 indicates we want a transparent background
+ if (c == 0)
+ view->setTransparent(true);
+}
+
+jclass WebViewCore::getPluginClass(const WTF::String& libName, const char* className)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+
+ jstring libString = wtfStringToJstring(env, libName);
+ jstring classString = env->NewStringUTF(className);
+ jobject pluginClass = env->CallObjectMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_getPluginClass,
+ libString, classString);
+ checkException(env);
+
+ // cleanup unneeded local JNI references
+ env->DeleteLocalRef(libString);
+ env->DeleteLocalRef(classString);
+
+ if (pluginClass != NULL) {
+ return static_cast<jclass>(pluginClass);
+ } else {
+ return NULL;
+ }
+}
+
+void WebViewCore::showFullScreenPlugin(jobject childView, NPP npp)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject obj = m_javaGlue->object(env);
+
+ env->CallVoidMethod(obj.get(),
+ m_javaGlue->m_showFullScreenPlugin, childView, (int)npp);
+ checkException(env);
+}
+
+void WebViewCore::hideFullScreenPlugin()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_hideFullScreenPlugin);
+ checkException(env);
+}
+
+jobject WebViewCore::createSurface(jobject view)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jobject result = env->CallObjectMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_createSurface, view);
+ checkException(env);
+ return result;
+}
+
+jobject WebViewCore::addSurface(jobject view, int x, int y, int width, int height)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jobject result = env->CallObjectMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_addSurface,
+ view, x, y, width, height);
+ checkException(env);
+ return result;
+}
+
+void WebViewCore::updateSurface(jobject childView, int x, int y, int width, int height)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_updateSurface, childView,
+ x, y, width, height);
+ checkException(env);
+}
+
+void WebViewCore::destroySurface(jobject childView)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_destroySurface, childView);
+ checkException(env);
+}
+
+jobject WebViewCore::getContext()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ AutoJObject obj = m_javaGlue->object(env);
+
+ jobject result = env->CallObjectMethod(obj.get(), m_javaGlue->m_getContext);
+ checkException(env);
+ return result;
+}
+
+void WebViewCore::keepScreenOn(bool screenOn) {
+ if ((screenOn && m_screenOnCounter == 0) || (!screenOn && m_screenOnCounter == 1)) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_keepScreenOn, screenOn);
+ checkException(env);
+ }
+
+ // update the counter
+ if (screenOn)
+ m_screenOnCounter++;
+ else if (m_screenOnCounter > 0)
+ m_screenOnCounter--;
+}
+
+bool WebViewCore::validNodeAndBounds(Frame* frame, Node* node,
+ const IntRect& originalAbsoluteBounds)
+{
+ bool valid = CacheBuilder::validNode(m_mainFrame, frame, node);
+ if (!valid)
+ return false;
+ RenderObject* renderer = node->renderer();
+ if (!renderer)
+ return false;
+ IntRect absBounds = node->hasTagName(HTMLNames::areaTag)
+ ? CacheBuilder::getAreaRect(static_cast<HTMLAreaElement*>(node))
+ : renderer->absoluteBoundingBoxRect();
+ return absBounds == originalAbsoluteBounds;
+}
+
+void WebViewCore::showRect(int left, int top, int width, int height,
+ int contentWidth, int contentHeight, float xPercentInDoc,
+ float xPercentInView, float yPercentInDoc, float yPercentInView)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_showRect,
+ left, top, width, height, contentWidth, contentHeight,
+ xPercentInDoc, xPercentInView, yPercentInDoc, yPercentInView);
+ checkException(env);
+}
+
+void WebViewCore::centerFitRect(int x, int y, int width, int height)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_centerFitRect, x, y, width, height);
+ checkException(env);
+}
+
+
+void WebViewCore::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_setScrollbarModes,
+ horizontalMode, verticalMode);
+ checkException(env);
+}
+
+void WebViewCore::notifyWebAppCanBeInstalled()
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_setInstallableWebApp);
+ checkException(env);
+}
+
+#if ENABLE(VIDEO)
+void WebViewCore::enterFullscreenForVideoLayer(int layerId, const WTF::String& url)
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring jUrlStr = wtfStringToJstring(env, url);
+ env->CallVoidMethod(m_javaGlue->object(env).get(),
+ m_javaGlue->m_enterFullscreenForVideoLayer, layerId, jUrlStr);
+ checkException(env);
+}
+#endif
+
+void WebViewCore::setWebTextViewAutoFillable(int queryId, const string16& previewSummary)
+{
+#if ENABLE(WEB_AUTOFILL)
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ jstring preview = env->NewString(previewSummary.data(), previewSummary.length());
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_setWebTextViewAutoFillable, queryId, preview);
+ env->DeleteLocalRef(preview);
+#endif
+}
+
+bool WebViewCore::drawIsPaused() const
+{
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ return env->GetBooleanField(m_javaGlue->object(env).get(),
+ gWebViewCoreFields.m_drawIsPaused);
+}
+
+#if USE(CHROME_NETWORK_STACK)
+void WebViewCore::setWebRequestContextUserAgent()
+{
+ // We cannot create a WebRequestContext, because we might not know it this is a private tab or not yet
+ if (m_webRequestContext)
+ m_webRequestContext->setUserAgent(WebFrame::getWebFrame(m_mainFrame)->userAgentForURL(0)); // URL not used
+}
+
+void WebViewCore::setWebRequestContextCacheMode(int cacheMode)
+{
+ m_cacheMode = cacheMode;
+ // We cannot create a WebRequestContext, because we might not know it this is a private tab or not yet
+ if (!m_webRequestContext)
+ return;
+
+ m_webRequestContext->setCacheMode(cacheMode);
+}
+
+WebRequestContext* WebViewCore::webRequestContext()
+{
+ if (!m_webRequestContext) {
+ Settings* settings = mainFrame()->settings();
+ m_webRequestContext = new WebRequestContext(settings && settings->privateBrowsingEnabled());
+ setWebRequestContextUserAgent();
+ setWebRequestContextCacheMode(m_cacheMode);
+ }
+ return m_webRequestContext.get();
+}
+#endif
+
+void WebViewCore::scrollRenderLayer(int layer, const SkRect& rect)
+{
+#if USE(ACCELERATED_COMPOSITING)
+ GraphicsLayerAndroid* root = graphicsRootLayer();
+ if (!root)
+ return;
+
+ LayerAndroid* layerAndroid = root->platformLayer();
+ if (!layerAndroid)
+ return;
+
+ LayerAndroid* target = layerAndroid->findById(layer);
+ if (!target)
+ return;
+
+ RenderLayer* owner = target->owningLayer();
+ if (!owner)
+ return;
+
+ if (owner->stackingContext())
+ owner->scrollToOffset(rect.fLeft, rect.fTop, true, false);
+#endif
+}
+
+//----------------------------------------------------------------------
+// Native JNI methods
+//----------------------------------------------------------------------
+static void RevealSelection(JNIEnv *env, jobject obj)
+{
+ GET_NATIVE_VIEW(env, obj)->revealSelection();
+}
+
+static jstring RequestLabel(JNIEnv *env, jobject obj, int framePointer,
+ int nodePointer)
+{
+ return wtfStringToJstring(env, GET_NATIVE_VIEW(env, obj)->requestLabel(
+ (WebCore::Frame*) framePointer, (WebCore::Node*) nodePointer));
+}
+
+static void ClearContent(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ viewImpl->clearContent();
+}
+
+static void UpdateFrameCacheIfLoading(JNIEnv *env, jobject obj)
+{
+ GET_NATIVE_VIEW(env, obj)->updateFrameCacheIfLoading();
+}
+
+static void SetSize(JNIEnv *env, jobject obj, jint width, jint height,
+ jint textWrapWidth, jfloat scale, jint screenWidth, jint screenHeight,
+ jint anchorX, jint anchorY, jboolean ignoreHeight)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOGV("webviewcore::nativeSetSize(%u %u)\n viewImpl: %p", (unsigned)width, (unsigned)height, viewImpl);
+ LOG_ASSERT(viewImpl, "viewImpl not set in nativeSetSize");
+ viewImpl->setSizeScreenWidthAndScale(width, height, textWrapWidth, scale,
+ screenWidth, screenHeight, anchorX, anchorY, ignoreHeight);
+}
+
+static void SetScrollOffset(JNIEnv *env, jobject obj, jint gen, jboolean sendScrollEvent, jint x, jint y)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "need viewImpl");
+
+ viewImpl->setScrollOffset(gen, sendScrollEvent, x, y);
+}
+
+static void SetGlobalBounds(JNIEnv *env, jobject obj, jint x, jint y, jint h,
+ jint v)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "need viewImpl");
+
+ viewImpl->setGlobalBounds(x, y, h, v);
+}
+
+static jboolean Key(JNIEnv *env, jobject obj, jint keyCode, jint unichar,
+ jint repeatCount, jboolean isShift, jboolean isAlt, jboolean isSym,
+ jboolean isDown)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ return GET_NATIVE_VIEW(env, obj)->key(PlatformKeyboardEvent(keyCode,
+ unichar, repeatCount, isDown, isShift, isAlt, isSym));
+}
+
+static void Click(JNIEnv *env, jobject obj, int framePtr, int nodePtr, jboolean fake)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in Click");
+
+ viewImpl->click(reinterpret_cast<WebCore::Frame*>(framePtr),
+ reinterpret_cast<WebCore::Node*>(nodePtr), fake);
+}
+
+static void ContentInvalidateAll(JNIEnv *env, jobject obj)
+{
+ GET_NATIVE_VIEW(env, obj)->contentInvalidateAll();
+}
+
+static void DeleteSelection(JNIEnv *env, jobject obj, jint start, jint end,
+ jint textGeneration)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ viewImpl->deleteSelection(start, end, textGeneration);
+}
+
+static void SetSelection(JNIEnv *env, jobject obj, jint start, jint end)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ viewImpl->setSelection(start, end);
+}
+
+static jstring ModifySelection(JNIEnv *env, jobject obj, jint direction, jint granularity)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ String selectionString = viewImpl->modifySelection(direction, granularity);
+ return wtfStringToJstring(env, selectionString);
+}
+
+static void ReplaceTextfieldText(JNIEnv *env, jobject obj,
+ jint oldStart, jint oldEnd, jstring replace, jint start, jint end,
+ jint textGeneration)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ WTF::String webcoreString = jstringToWtfString(env, replace);
+ viewImpl->replaceTextfieldText(oldStart,
+ oldEnd, webcoreString, start, end, textGeneration);
+}
+
+static void PassToJs(JNIEnv *env, jobject obj,
+ jint generation, jstring currentText, jint keyCode,
+ jint keyValue, jboolean down, jboolean cap, jboolean fn, jboolean sym)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WTF::String current = jstringToWtfString(env, currentText);
+ GET_NATIVE_VIEW(env, obj)->passToJs(generation, current,
+ PlatformKeyboardEvent(keyCode, keyValue, 0, down, cap, fn, sym));
+}
+
+static void ScrollFocusedTextInput(JNIEnv *env, jobject obj, jfloat xPercent,
+ jint y)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ viewImpl->scrollFocusedTextInput(xPercent, y);
+}
+
+static void SetFocusControllerActive(JNIEnv *env, jobject obj, jboolean active)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ LOGV("webviewcore::nativeSetFocusControllerActive()\n");
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in nativeSetFocusControllerActive");
+ viewImpl->setFocusControllerActive(active);
+}
+
+static void SaveDocumentState(JNIEnv *env, jobject obj, jint frame)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ LOGV("webviewcore::nativeSaveDocumentState()\n");
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in nativeSaveDocumentState");
+ viewImpl->saveDocumentState((WebCore::Frame*) frame);
+}
+
+void WebViewCore::addVisitedLink(const UChar* string, int length)
+{
+ if (m_groupForVisitedLinks)
+ m_groupForVisitedLinks->addVisitedLink(string, length);
+}
+
+static jint UpdateLayers(JNIEnv *env, jobject obj, jobject region)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ BaseLayerAndroid* result = viewImpl->createBaseLayer();
+ SkRegion* nativeRegion = GraphicsJNI::getNativeRegion(env, region);
+ if (result) {
+ SkIRect bounds;
+ LayerAndroid* root = static_cast<LayerAndroid*>(result->getChild(0));
+ if (root) {
+ root->bounds().roundOut(&bounds);
+ nativeRegion->setRect(bounds);
+ }
+ }
+ return reinterpret_cast<jint>(result);
+}
+
+static jint RecordContent(JNIEnv *env, jobject obj, jobject region, jobject pt)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ SkRegion* nativeRegion = GraphicsJNI::getNativeRegion(env, region);
+ SkIPoint nativePt;
+ BaseLayerAndroid* result = viewImpl->recordContent(nativeRegion, &nativePt);
+ GraphicsJNI::ipoint_to_jpoint(nativePt, env, pt);
+ return reinterpret_cast<jint>(result);
+}
+
+static void SplitContent(JNIEnv *env, jobject obj, jint content)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ viewImpl->splitContent(reinterpret_cast<PictureSet*>(content));
+}
+
+static void SendListBoxChoice(JNIEnv* env, jobject obj, jint choice)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in nativeSendListBoxChoice");
+ viewImpl->popupReply(choice);
+}
+
+// Set aside a predetermined amount of space in which to place the listbox
+// choices, to avoid unnecessary allocations.
+// The size here is arbitrary. We want the size to be at least as great as the
+// number of items in the average multiple-select listbox.
+#define PREPARED_LISTBOX_STORAGE 10
+
+static void SendListBoxChoices(JNIEnv* env, jobject obj, jbooleanArray jArray,
+ jint size)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in nativeSendListBoxChoices");
+ jboolean* ptrArray = env->GetBooleanArrayElements(jArray, 0);
+ SkAutoSTMalloc<PREPARED_LISTBOX_STORAGE, int> storage(size);
+ int* array = storage.get();
+ int count = 0;
+ for (int i = 0; i < size; i++) {
+ if (ptrArray[i]) {
+ array[count++] = i;
+ }
+ }
+ env->ReleaseBooleanArrayElements(jArray, ptrArray, JNI_ABORT);
+ viewImpl->popupReply(array, count);
+}
+
+static jstring FindAddress(JNIEnv *env, jobject obj, jstring addr,
+ jboolean caseInsensitive)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ if (!addr)
+ return 0;
+ int length = env->GetStringLength(addr);
+ if (!length)
+ return 0;
+ const jchar* addrChars = env->GetStringChars(addr, 0);
+ int start, end;
+ bool success = CacheBuilder::FindAddress(addrChars, length,
+ &start, &end, caseInsensitive) == CacheBuilder::FOUND_COMPLETE;
+ jstring ret = 0;
+ if (success)
+ ret = env->NewString(addrChars + start, end - start);
+ env->ReleaseStringChars(addr, addrChars);
+ return ret;
+}
+
+static jboolean HandleTouchEvent(JNIEnv *env, jobject obj, jint action, jintArray idArray,
+ jintArray xArray, jintArray yArray,
+ jint count, jint actionIndex, jint metaState)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ jint* ptrIdArray = env->GetIntArrayElements(idArray, 0);
+ jint* ptrXArray = env->GetIntArrayElements(xArray, 0);
+ jint* ptrYArray = env->GetIntArrayElements(yArray, 0);
+ Vector<int> ids(count);
+ Vector<IntPoint> points(count);
+ for (int c = 0; c < count; c++) {
+ ids[c] = ptrIdArray[c];
+ points[c].setX(ptrXArray[c]);
+ points[c].setY(ptrYArray[c]);
+ }
+ env->ReleaseIntArrayElements(idArray, ptrIdArray, JNI_ABORT);
+ env->ReleaseIntArrayElements(xArray, ptrXArray, JNI_ABORT);
+ env->ReleaseIntArrayElements(yArray, ptrYArray, JNI_ABORT);
+
+ return viewImpl->handleTouchEvent(action, ids, points, actionIndex, metaState);
+}
+
+static void TouchUp(JNIEnv *env, jobject obj, jint touchGeneration,
+ jint frame, jint node, jint x, jint y)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ viewImpl->touchUp(touchGeneration,
+ (WebCore::Frame*) frame, (WebCore::Node*) node, x, y);
+}
+
+static jstring RetrieveHref(JNIEnv *env, jobject obj, jint x, jint y)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ WTF::String result = viewImpl->retrieveHref(x, y);
+ if (!result.isEmpty())
+ return wtfStringToJstring(env, result);
+ return 0;
+}
+
+static jstring RetrieveAnchorText(JNIEnv *env, jobject obj, jint x, jint y)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ WTF::String result = viewImpl->retrieveAnchorText(x, y);
+ if (!result.isEmpty())
+ return wtfStringToJstring(env, result);
+ return 0;
+}
+
+static jstring RetrieveImageSource(JNIEnv *env, jobject obj, jint x, jint y)
+{
+ WTF::String result = GET_NATIVE_VIEW(env, obj)->retrieveImageSource(x, y);
+ return !result.isEmpty() ? wtfStringToJstring(env, result) : 0;
+}
+
+static void StopPaintingCaret(JNIEnv *env, jobject obj)
+{
+ GET_NATIVE_VIEW(env, obj)->setShouldPaintCaret(false);
+}
+
+static void MoveFocus(JNIEnv *env, jobject obj, jint framePtr, jint nodePtr)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ viewImpl->moveFocus((WebCore::Frame*) framePtr, (WebCore::Node*) nodePtr);
+}
+
+static void MoveMouse(JNIEnv *env, jobject obj, jint frame,
+ jint x, jint y)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ viewImpl->moveMouse((WebCore::Frame*) frame, x, y);
+}
+
+static void MoveMouseIfLatest(JNIEnv *env, jobject obj, jint moveGeneration,
+ jint frame, jint x, jint y)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ viewImpl->moveMouseIfLatest(moveGeneration,
+ (WebCore::Frame*) frame, x, y);
+}
+
+static void UpdateFrameCache(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ viewImpl->updateFrameCache();
+}
+
+static jint GetContentMinPrefWidth(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+
+ WebCore::Frame* frame = viewImpl->mainFrame();
+ if (frame) {
+ WebCore::Document* document = frame->document();
+ if (document) {
+ WebCore::RenderObject* renderer = document->renderer();
+ if (renderer && renderer->isRenderView()) {
+ return renderer->minPreferredLogicalWidth();
+ }
+ }
+ }
+ return 0;
+}
+
+static void SetViewportSettingsFromNative(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+
+ WebCore::Settings* s = viewImpl->mainFrame()->page()->settings();
+ if (!s)
+ return;
+
+#ifdef ANDROID_META_SUPPORT
+ env->SetIntField(obj, gWebViewCoreFields.m_viewportWidth, s->viewportWidth());
+ env->SetIntField(obj, gWebViewCoreFields.m_viewportHeight, s->viewportHeight());
+ env->SetIntField(obj, gWebViewCoreFields.m_viewportInitialScale, s->viewportInitialScale());
+ env->SetIntField(obj, gWebViewCoreFields.m_viewportMinimumScale, s->viewportMinimumScale());
+ env->SetIntField(obj, gWebViewCoreFields.m_viewportMaximumScale, s->viewportMaximumScale());
+ env->SetBooleanField(obj, gWebViewCoreFields.m_viewportUserScalable, s->viewportUserScalable());
+ env->SetIntField(obj, gWebViewCoreFields.m_viewportDensityDpi, s->viewportTargetDensityDpi());
+#endif
+}
+
+static void SetBackgroundColor(JNIEnv *env, jobject obj, jint color)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+
+ viewImpl->setBackgroundColor((SkColor) color);
+}
+
+static void DumpDomTree(JNIEnv *env, jobject obj, jboolean useFile)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+
+ viewImpl->dumpDomTree(useFile);
+}
+
+static void DumpRenderTree(JNIEnv *env, jobject obj, jboolean useFile)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+
+ viewImpl->dumpRenderTree(useFile);
+}
+
+static void DumpNavTree(JNIEnv *env, jobject obj)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+
+ viewImpl->dumpNavTree();
+}
+
+static void DumpV8Counters(JNIEnv*, jobject)
+{
+#if USE(V8)
+#ifdef ANDROID_INSTRUMENT
+ V8Counters::dumpCounters();
+#endif
+#endif
+}
+
+static void SetJsFlags(JNIEnv *env, jobject obj, jstring flags)
+{
+#if USE(V8)
+ WTF::String flagsString = jstringToWtfString(env, flags);
+ WTF::CString utf8String = flagsString.utf8();
+ WebCore::ScriptController::setFlags(utf8String.data(), utf8String.length());
+#endif
+}
+
+
+// Called from the Java side to set a new quota for the origin or new appcache
+// max size in response to a notification that the original quota was exceeded or
+// that the appcache has reached its maximum size.
+static void SetNewStorageLimit(JNIEnv* env, jobject obj, jlong quota) {
+#if ENABLE(DATABASE) || ENABLE(OFFLINE_WEB_APPLICATIONS)
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ Frame* frame = viewImpl->mainFrame();
+
+ // The main thread is blocked awaiting this response, so now we can wake it
+ // up.
+ ChromeClientAndroid* chromeC = static_cast<ChromeClientAndroid*>(frame->page()->chrome()->client());
+ chromeC->wakeUpMainThreadWithNewQuota(quota);
+#endif
+}
+
+// Called from Java to provide a Geolocation permission state for the specified origin.
+static void GeolocationPermissionsProvide(JNIEnv* env, jobject obj, jstring origin, jboolean allow, jboolean remember) {
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ Frame* frame = viewImpl->mainFrame();
+
+ ChromeClientAndroid* chromeClient = static_cast<ChromeClientAndroid*>(frame->page()->chrome()->client());
+ chromeClient->provideGeolocationPermissions(jstringToWtfString(env, origin), allow, remember);
+}
+
+static void RegisterURLSchemeAsLocal(JNIEnv* env, jobject obj, jstring scheme) {
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterAuto counter(TimeCounter::WebViewCoreTimeCounter);
+#endif
+ WebCore::SchemeRegistry::registerURLSchemeAsLocal(jstringToWtfString(env, scheme));
+}
+
+static bool FocusBoundsChanged(JNIEnv* env, jobject obj)
+{
+ return GET_NATIVE_VIEW(env, obj)->focusBoundsChanged();
+}
+
+static void Pause(JNIEnv* env, jobject obj)
+{
+ // This is called for the foreground tab when the browser is put to the
+ // background (and also for any tab when it is put to the background of the
+ // browser). The browser can only be killed by the system when it is in the
+ // background, so saving the Geolocation permission state now ensures that
+ // is maintained when the browser is killed.
+ ChromeClient* chromeClient = GET_NATIVE_VIEW(env, obj)->mainFrame()->page()->chrome()->client();
+ ChromeClientAndroid* chromeClientAndroid = static_cast<ChromeClientAndroid*>(chromeClient);
+ chromeClientAndroid->storeGeolocationPermissions();
+
+ Frame* mainFrame = GET_NATIVE_VIEW(env, obj)->mainFrame();
+ for (Frame* frame = mainFrame; frame; frame = frame->tree()->traverseNext()) {
+ Geolocation* geolocation = frame->domWindow()->navigator()->optionalGeolocation();
+ if (geolocation)
+ geolocation->suspend();
+ }
+
+ GET_NATIVE_VIEW(env, obj)->deviceMotionAndOrientationManager()->maybeSuspendClients();
+
+ ANPEvent event;
+ SkANP::InitEvent(&event, kLifecycle_ANPEventType);
+ event.data.lifecycle.action = kPause_ANPLifecycleAction;
+ GET_NATIVE_VIEW(env, obj)->sendPluginEvent(event);
+
+ GET_NATIVE_VIEW(env, obj)->setIsPaused(true);
+}
+
+static void Resume(JNIEnv* env, jobject obj)
+{
+ Frame* mainFrame = GET_NATIVE_VIEW(env, obj)->mainFrame();
+ for (Frame* frame = mainFrame; frame; frame = frame->tree()->traverseNext()) {
+ Geolocation* geolocation = frame->domWindow()->navigator()->optionalGeolocation();
+ if (geolocation)
+ geolocation->resume();
+ }
+
+ GET_NATIVE_VIEW(env, obj)->deviceMotionAndOrientationManager()->maybeResumeClients();
+
+ ANPEvent event;
+ SkANP::InitEvent(&event, kLifecycle_ANPEventType);
+ event.data.lifecycle.action = kResume_ANPLifecycleAction;
+ GET_NATIVE_VIEW(env, obj)->sendPluginEvent(event);
+
+ GET_NATIVE_VIEW(env, obj)->setIsPaused(false);
+}
+
+static void FreeMemory(JNIEnv* env, jobject obj)
+{
+ ANPEvent event;
+ SkANP::InitEvent(&event, kLifecycle_ANPEventType);
+ event.data.lifecycle.action = kFreeMemory_ANPLifecycleAction;
+ GET_NATIVE_VIEW(env, obj)->sendPluginEvent(event);
+}
+
+static void ProvideVisitedHistory(JNIEnv *env, jobject obj, jobject hist)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+
+ jobjectArray array = static_cast<jobjectArray>(hist);
+
+ jsize len = env->GetArrayLength(array);
+ for (jsize i = 0; i < len; i++) {
+ jstring item = static_cast<jstring>(env->GetObjectArrayElement(array, i));
+ const UChar* str = static_cast<const UChar*>(env->GetStringChars(item, 0));
+ jsize len = env->GetStringLength(item);
+ viewImpl->addVisitedLink(str, len);
+ env->ReleaseStringChars(item, str);
+ env->DeleteLocalRef(item);
+ }
+}
+
+// Notification from the UI thread that the plugin's full-screen surface has been discarded
+static void FullScreenPluginHidden(JNIEnv* env, jobject obj, jint npp)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ PluginWidgetAndroid* plugin = viewImpl->getPluginWidget((NPP)npp);
+ if (plugin)
+ plugin->exitFullScreen(false);
+}
+
+static WebCore::IntRect jrect_to_webrect(JNIEnv* env, jobject obj)
+{
+ int L, T, R, B;
+ GraphicsJNI::get_jrect(env, obj, &L, &T, &R, &B);
+ return WebCore::IntRect(L, T, R - L, B - T);
+}
+
+static bool ValidNodeAndBounds(JNIEnv *env, jobject obj, int frame, int node,
+ jobject rect)
+{
+ IntRect nativeRect = jrect_to_webrect(env, rect);
+ return GET_NATIVE_VIEW(env, obj)->validNodeAndBounds(
+ reinterpret_cast<Frame*>(frame),
+ reinterpret_cast<Node*>(node), nativeRect);
+}
+
+static jobject GetTouchHighlightRects(JNIEnv* env, jobject obj, jint x, jint y, jint slop)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ if (!viewImpl)
+ return 0;
+ Vector<IntRect> rects = viewImpl->getTouchHighlightRects(x, y, slop);
+ if (rects.isEmpty())
+ return 0;
+
+ jclass arrayClass = env->FindClass("java/util/ArrayList");
+ LOG_ASSERT(arrayClass, "Could not find java/util/ArrayList");
+ jmethodID init = env->GetMethodID(arrayClass, "<init>", "(I)V");
+ LOG_ASSERT(init, "Could not find constructor for ArrayList");
+ jobject array = env->NewObject(arrayClass, init, rects.size());
+ LOG_ASSERT(array, "Could not create a new ArrayList");
+ jmethodID add = env->GetMethodID(arrayClass, "add", "(Ljava/lang/Object;)Z");
+ LOG_ASSERT(add, "Could not find add method on ArrayList");
+ jclass rectClass = env->FindClass("android/graphics/Rect");
+ LOG_ASSERT(rectClass, "Could not find android/graphics/Rect");
+ jmethodID rectinit = env->GetMethodID(rectClass, "<init>", "(IIII)V");
+ LOG_ASSERT(rectinit, "Could not find init method on Rect");
+
+ for (size_t i = 0; i < rects.size(); i++) {
+ jobject rect = env->NewObject(rectClass, rectinit, rects[i].x(),
+ rects[i].y(), rects[i].right(), rects[i].bottom());
+ if (rect) {
+ env->CallBooleanMethod(array, add, rect);
+ env->DeleteLocalRef(rect);
+ }
+ }
+
+ env->DeleteLocalRef(rectClass);
+ env->DeleteLocalRef(arrayClass);
+ return array;
+}
+
+static void AutoFillForm(JNIEnv* env, jobject obj, jint queryId)
+{
+#if ENABLE(WEB_AUTOFILL)
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ if (!viewImpl)
+ return;
+
+ WebCore::Frame* frame = viewImpl->mainFrame();
+ if (frame) {
+ EditorClientAndroid* editorC = static_cast<EditorClientAndroid*>(frame->page()->editorClient());
+ WebAutoFill* autoFill = editorC->getAutoFill();
+ autoFill->fillFormFields(queryId);
+ }
+#endif
+}
+
+static void ScrollRenderLayer(JNIEnv* env, jobject obj, jint layer, jobject jRect)
+{
+ SkRect rect;
+ GraphicsJNI::jrect_to_rect(env, jRect, &rect);
+ GET_NATIVE_VIEW(env, obj)->scrollRenderLayer(layer, rect);
+}
+
+// ----------------------------------------------------------------------------
+
+/*
+ * JNI registration.
+ */
+static JNINativeMethod gJavaWebViewCoreMethods[] = {
+ { "nativeClearContent", "()V",
+ (void*) ClearContent },
+ { "nativeFocusBoundsChanged", "()Z",
+ (void*) FocusBoundsChanged } ,
+ { "nativeKey", "(IIIZZZZ)Z",
+ (void*) Key },
+ { "nativeClick", "(IIZ)V",
+ (void*) Click },
+ { "nativeContentInvalidateAll", "()V",
+ (void*) ContentInvalidateAll },
+ { "nativeSendListBoxChoices", "([ZI)V",
+ (void*) SendListBoxChoices },
+ { "nativeSendListBoxChoice", "(I)V",
+ (void*) SendListBoxChoice },
+ { "nativeSetSize", "(IIIFIIIIZ)V",
+ (void*) SetSize },
+ { "nativeSetScrollOffset", "(IZII)V",
+ (void*) SetScrollOffset },
+ { "nativeSetGlobalBounds", "(IIII)V",
+ (void*) SetGlobalBounds },
+ { "nativeSetSelection", "(II)V",
+ (void*) SetSelection } ,
+ { "nativeModifySelection", "(II)Ljava/lang/String;",
+ (void*) ModifySelection },
+ { "nativeDeleteSelection", "(III)V",
+ (void*) DeleteSelection } ,
+ { "nativeReplaceTextfieldText", "(IILjava/lang/String;III)V",
+ (void*) ReplaceTextfieldText } ,
+ { "nativeMoveFocus", "(II)V",
+ (void*) MoveFocus },
+ { "nativeMoveMouse", "(III)V",
+ (void*) MoveMouse },
+ { "nativeMoveMouseIfLatest", "(IIII)V",
+ (void*) MoveMouseIfLatest },
+ { "passToJs", "(ILjava/lang/String;IIZZZZ)V",
+ (void*) PassToJs },
+ { "nativeScrollFocusedTextInput", "(FI)V",
+ (void*) ScrollFocusedTextInput },
+ { "nativeSetFocusControllerActive", "(Z)V",
+ (void*) SetFocusControllerActive },
+ { "nativeSaveDocumentState", "(I)V",
+ (void*) SaveDocumentState },
+ { "nativeFindAddress", "(Ljava/lang/String;Z)Ljava/lang/String;",
+ (void*) FindAddress },
+ { "nativeHandleTouchEvent", "(I[I[I[IIII)Z",
+ (void*) HandleTouchEvent },
+ { "nativeTouchUp", "(IIIII)V",
+ (void*) TouchUp },
+ { "nativeRetrieveHref", "(II)Ljava/lang/String;",
+ (void*) RetrieveHref },
+ { "nativeRetrieveAnchorText", "(II)Ljava/lang/String;",
+ (void*) RetrieveAnchorText },
+ { "nativeRetrieveImageSource", "(II)Ljava/lang/String;",
+ (void*) RetrieveImageSource },
+ { "nativeStopPaintingCaret", "()V",
+ (void*) StopPaintingCaret },
+ { "nativeUpdateFrameCache", "()V",
+ (void*) UpdateFrameCache },
+ { "nativeGetContentMinPrefWidth", "()I",
+ (void*) GetContentMinPrefWidth },
+ { "nativeUpdateLayers", "(Landroid/graphics/Region;)I",
+ (void*) UpdateLayers },
+ { "nativeRecordContent", "(Landroid/graphics/Region;Landroid/graphics/Point;)I",
+ (void*) RecordContent },
+ { "setViewportSettingsFromNative", "()V",
+ (void*) SetViewportSettingsFromNative },
+ { "nativeSplitContent", "(I)V",
+ (void*) SplitContent },
+ { "nativeSetBackgroundColor", "(I)V",
+ (void*) SetBackgroundColor },
+ { "nativeRegisterURLSchemeAsLocal", "(Ljava/lang/String;)V",
+ (void*) RegisterURLSchemeAsLocal },
+ { "nativeDumpDomTree", "(Z)V",
+ (void*) DumpDomTree },
+ { "nativeDumpRenderTree", "(Z)V",
+ (void*) DumpRenderTree },
+ { "nativeDumpNavTree", "()V",
+ (void*) DumpNavTree },
+ { "nativeDumpV8Counters", "()V",
+ (void*) DumpV8Counters },
+ { "nativeSetNewStorageLimit", "(J)V",
+ (void*) SetNewStorageLimit },
+ { "nativeGeolocationPermissionsProvide", "(Ljava/lang/String;ZZ)V",
+ (void*) GeolocationPermissionsProvide },
+ { "nativePause", "()V", (void*) Pause },
+ { "nativeResume", "()V", (void*) Resume },
+ { "nativeFreeMemory", "()V", (void*) FreeMemory },
+ { "nativeSetJsFlags", "(Ljava/lang/String;)V", (void*) SetJsFlags },
+ { "nativeRequestLabel", "(II)Ljava/lang/String;",
+ (void*) RequestLabel },
+ { "nativeRevealSelection", "()V", (void*) RevealSelection },
+ { "nativeUpdateFrameCacheIfLoading", "()V",
+ (void*) UpdateFrameCacheIfLoading },
+ { "nativeProvideVisitedHistory", "([Ljava/lang/String;)V",
+ (void*) ProvideVisitedHistory },
+ { "nativeFullScreenPluginHidden", "(I)V",
+ (void*) FullScreenPluginHidden },
+ { "nativeValidNodeAndBounds", "(IILandroid/graphics/Rect;)Z",
+ (void*) ValidNodeAndBounds },
+ { "nativeGetTouchHighlightRects", "(III)Ljava/util/ArrayList;",
+ (void*) GetTouchHighlightRects },
+ { "nativeAutoFillForm", "(I)V",
+ (void*) AutoFillForm },
+ { "nativeScrollLayer", "(ILandroid/graphics/Rect;)V",
+ (void*) ScrollRenderLayer },
+};
+
+int registerWebViewCore(JNIEnv* env)
+{
+ jclass widget = env->FindClass("android/webkit/WebViewCore");
+ LOG_ASSERT(widget,
+ "Unable to find class android/webkit/WebViewCore");
+ gWebViewCoreFields.m_nativeClass = env->GetFieldID(widget, "mNativeClass",
+ "I");
+ LOG_ASSERT(gWebViewCoreFields.m_nativeClass,
+ "Unable to find android/webkit/WebViewCore.mNativeClass");
+ gWebViewCoreFields.m_viewportWidth = env->GetFieldID(widget,
+ "mViewportWidth", "I");
+ LOG_ASSERT(gWebViewCoreFields.m_viewportWidth,
+ "Unable to find android/webkit/WebViewCore.mViewportWidth");
+ gWebViewCoreFields.m_viewportHeight = env->GetFieldID(widget,
+ "mViewportHeight", "I");
+ LOG_ASSERT(gWebViewCoreFields.m_viewportHeight,
+ "Unable to find android/webkit/WebViewCore.mViewportHeight");
+ gWebViewCoreFields.m_viewportInitialScale = env->GetFieldID(widget,
+ "mViewportInitialScale", "I");
+ LOG_ASSERT(gWebViewCoreFields.m_viewportInitialScale,
+ "Unable to find android/webkit/WebViewCore.mViewportInitialScale");
+ gWebViewCoreFields.m_viewportMinimumScale = env->GetFieldID(widget,
+ "mViewportMinimumScale", "I");
+ LOG_ASSERT(gWebViewCoreFields.m_viewportMinimumScale,
+ "Unable to find android/webkit/WebViewCore.mViewportMinimumScale");
+ gWebViewCoreFields.m_viewportMaximumScale = env->GetFieldID(widget,
+ "mViewportMaximumScale", "I");
+ LOG_ASSERT(gWebViewCoreFields.m_viewportMaximumScale,
+ "Unable to find android/webkit/WebViewCore.mViewportMaximumScale");
+ gWebViewCoreFields.m_viewportUserScalable = env->GetFieldID(widget,
+ "mViewportUserScalable", "Z");
+ LOG_ASSERT(gWebViewCoreFields.m_viewportUserScalable,
+ "Unable to find android/webkit/WebViewCore.mViewportUserScalable");
+ gWebViewCoreFields.m_viewportDensityDpi = env->GetFieldID(widget,
+ "mViewportDensityDpi", "I");
+ LOG_ASSERT(gWebViewCoreFields.m_viewportDensityDpi,
+ "Unable to find android/webkit/WebViewCore.mViewportDensityDpi");
+ gWebViewCoreFields.m_webView = env->GetFieldID(widget,
+ "mWebView", "Landroid/webkit/WebView;");
+ LOG_ASSERT(gWebViewCoreFields.m_webView,
+ "Unable to find android/webkit/WebViewCore.mWebView");
+ gWebViewCoreFields.m_drawIsPaused = env->GetFieldID(widget,
+ "mDrawIsPaused", "Z");
+ LOG_ASSERT(gWebViewCoreFields.m_drawIsPaused,
+ "Unable to find android/webkit/WebViewCore.mDrawIsPaused");
+ gWebViewCoreFields.m_lowMemoryUsageMb = env->GetFieldID(widget, "mLowMemoryUsageThresholdMb", "I");
+ gWebViewCoreFields.m_highMemoryUsageMb = env->GetFieldID(widget, "mHighMemoryUsageThresholdMb", "I");
+ gWebViewCoreFields.m_highUsageDeltaMb = env->GetFieldID(widget, "mHighUsageDeltaMb", "I");
+
+ gWebViewCoreStaticMethods.m_isSupportedMediaMimeType =
+ env->GetStaticMethodID(widget, "isSupportedMediaMimeType", "(Ljava/lang/String;)Z");
+ LOG_FATAL_IF(!gWebViewCoreStaticMethods.m_isSupportedMediaMimeType,
+ "Could not find static method isSupportedMediaMimeType from WebViewCore");
+
+ env->DeleteLocalRef(widget);
+
+ return jniRegisterNativeMethods(env, "android/webkit/WebViewCore",
+ gJavaWebViewCoreMethods, NELEM(gJavaWebViewCoreMethods));
+}
+
+} /* namespace android */
diff --git a/Source/WebKit/android/jni/WebViewCore.h b/Source/WebKit/android/jni/WebViewCore.h
new file mode 100644
index 0000000..8d8f303
--- /dev/null
+++ b/Source/WebKit/android/jni/WebViewCore.h
@@ -0,0 +1,714 @@
+/*
+ * 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.
+ */
+
+#ifndef WEBVIEWCORE_H
+#define WEBVIEWCORE_H
+
+#include "CacheBuilder.h"
+#include "CachedHistory.h"
+#include "DeviceMotionAndOrientationManager.h"
+#include "DOMSelection.h"
+#include "FileChooser.h"
+#include "PictureSet.h"
+#include "PlatformGraphicsContext.h"
+#include "SkColor.h"
+#include "SkTDArray.h"
+#include "SkRegion.h"
+#include "Timer.h"
+#include "WebCoreRefObject.h"
+#include "WebCoreJni.h"
+#include "WebRequestContext.h"
+#include "android_npapi.h"
+
+#include <jni.h>
+#include <ui/KeycodeLabels.h>
+#include <ui/PixelFormat.h>
+
+namespace WebCore {
+ class Color;
+ class FrameView;
+ class HTMLAnchorElement;
+ class HTMLElement;
+ class HTMLImageElement;
+ class HTMLSelectElement;
+ class RenderPart;
+ class RenderText;
+ class Node;
+ class PlatformKeyboardEvent;
+ class QualifiedName;
+ class RenderTextControl;
+ class ScrollView;
+ class TimerBase;
+ class PageGroup;
+}
+
+#if USE(ACCELERATED_COMPOSITING)
+namespace WebCore {
+ class GraphicsLayerAndroid;
+}
+#endif
+
+namespace WebCore {
+ class BaseLayerAndroid;
+}
+
+struct PluginWidgetAndroid;
+class SkPicture;
+class SkIRect;
+
+namespace android {
+
+ enum Direction {
+ DIRECTION_BACKWARD = 0,
+ DIRECTION_FORWARD = 1
+ };
+
+ enum NavigationAxis {
+ AXIS_CHARACTER = 0,
+ AXIS_WORD = 1,
+ AXIS_SENTENCE = 2,
+ AXIS_HEADING = 3,
+ AXIS_SIBLING = 4,
+ AXIS_PARENT_FIRST_CHILD = 5,
+ AXIS_DOCUMENT = 6
+ };
+
+ class CachedFrame;
+ class CachedNode;
+ class CachedRoot;
+ class ListBoxReply;
+
+ class WebCoreReply : public WebCoreRefObject {
+ public:
+ virtual ~WebCoreReply() {}
+
+ virtual void replyInt(int value) {
+ SkDEBUGF(("WebCoreReply::replyInt(%d) not handled\n", value));
+ }
+
+ virtual void replyIntArray(const int* array, int count) {
+ SkDEBUGF(("WebCoreReply::replyIntArray() not handled\n"));
+ }
+ // add more replyFoo signatures as needed
+ };
+
+ // one instance of WebViewCore per page for calling into Java's WebViewCore
+ class WebViewCore : public WebCoreRefObject {
+ public:
+ /**
+ * Initialize the native WebViewCore with a JNI environment, a Java
+ * WebViewCore object and the main frame.
+ */
+ WebViewCore(JNIEnv* env, jobject javaView, WebCore::Frame* mainframe);
+ ~WebViewCore();
+
+ // helper function
+ static WebViewCore* getWebViewCore(const WebCore::FrameView* view);
+ static WebViewCore* getWebViewCore(const WebCore::ScrollView* view);
+
+ // Followings are called from native WebCore to Java
+
+ /**
+ * Notification that a form was blurred. Pass a message to hide the
+ * keyboard if it was showing for that Node.
+ * @param Node The Node that blurred.
+ */
+ void formDidBlur(const WebCore::Node*);
+ void focusNodeChanged(const WebCore::Node*);
+
+ /**
+ * Scroll to an absolute position.
+ * @param x The x coordinate.
+ * @param y The y coordinate.
+ * @param animate If it is true, animate to the new scroll position
+ *
+ * This method calls Java to trigger a gradual scroll event.
+ */
+ void scrollTo(int x, int y, bool animate = false);
+
+ /**
+ * Record the invalid rectangle
+ */
+ void contentInvalidate(const WebCore::IntRect &rect);
+ void contentInvalidateAll();
+
+ /**
+ * Satisfy any outstanding invalidates, so that the current state
+ * of the DOM is drawn.
+ */
+ void contentDraw();
+
+ /**
+ * copy the layers to the UI side
+ */
+ void layersDraw();
+
+#if USE(ACCELERATED_COMPOSITING)
+ GraphicsLayerAndroid* graphicsRootLayer() const;
+#endif
+
+ /** Invalidate the view/screen, NOT the content/DOM, but expressed in
+ * content/DOM coordinates (i.e. they need to eventually be scaled,
+ * by webview into view.java coordinates
+ */
+ void viewInvalidate(const WebCore::IntRect& rect);
+
+ /**
+ * Invalidate part of the content that may be offscreen at the moment
+ */
+ void offInvalidate(const WebCore::IntRect &rect);
+
+ /**
+ * Called by webcore when the progress indicator is done
+ * used to rebuild and display any changes in focus
+ */
+ void notifyProgressFinished();
+
+ /**
+ * Notify the view that WebCore did its first layout.
+ */
+ void didFirstLayout();
+
+ /**
+ * Notify the view to update the viewport.
+ */
+ void updateViewport();
+
+ /**
+ * Notify the view to restore the screen width, which in turn restores
+ * the scale. Also restore the scale for the text wrap.
+ */
+ void restoreScale(float scale, float textWrapScale);
+
+ /**
+ * Tell the java side to update the focused textfield
+ * @param pointer Pointer to the node for the input field.
+ * @param changeToPassword If true, we are changing the textfield to
+ * a password field, and ignore the String
+ * @param text If changeToPassword is false, this is the new text that
+ * should go into the textfield.
+ */
+ void updateTextfield(WebCore::Node* pointer,
+ bool changeToPassword, const WTF::String& text);
+
+ /**
+ * Tell the java side to update the current selection in the focused
+ * textfield to the WebTextView. This function finds the currently
+ * focused textinput, and passes its selection to java.
+ * If there is no focus, or it is not a text input, this does nothing.
+ */
+ void updateTextSelection();
+
+ void clearTextEntry();
+ // JavaScript support
+ void jsAlert(const WTF::String& url, const WTF::String& text);
+ bool jsConfirm(const WTF::String& url, const WTF::String& text);
+ bool jsPrompt(const WTF::String& url, const WTF::String& message,
+ const WTF::String& defaultValue, WTF::String& result);
+ bool jsUnload(const WTF::String& url, const WTF::String& message);
+ bool jsInterrupt();
+
+ /**
+ * Tell the Java side that the origin has exceeded its database quota.
+ * @param url The URL of the page that caused the quota overflow
+ * @param databaseIdentifier the id of the database that caused the
+ * quota overflow.
+ * @param currentQuota The current quota for the origin
+ * @param estimatedSize The estimated size of the database
+ */
+ void exceededDatabaseQuota(const WTF::String& url,
+ const WTF::String& databaseIdentifier,
+ const unsigned long long currentQuota,
+ const unsigned long long estimatedSize);
+
+ /**
+ * Tell the Java side that the appcache has exceeded its max size.
+ * @param spaceNeeded is the amount of disk space that would be needed
+ * in order for the last appcache operation to succeed.
+ */
+ void reachedMaxAppCacheSize(const unsigned long long spaceNeeded);
+
+ /**
+ * Set up the PageGroup's idea of which links have been visited,
+ * with the browser history.
+ * @param group the object to deliver the links to.
+ */
+ void populateVisitedLinks(WebCore::PageGroup*);
+
+ /**
+ * Instruct the browser to show a Geolocation permission prompt for the
+ * specified origin.
+ * @param origin The origin of the frame requesting Geolocation
+ * permissions.
+ */
+ void geolocationPermissionsShowPrompt(const WTF::String& origin);
+ /**
+ * Instruct the browser to hide the Geolocation permission prompt.
+ */
+ void geolocationPermissionsHidePrompt();
+
+ jobject getDeviceMotionService();
+ jobject getDeviceOrientationService();
+
+ void addMessageToConsole(const String& message, unsigned int lineNumber, const String& sourceID, int msgLevel);
+
+ /**
+ * Tell the Java side of the scrollbar mode
+ */
+ void setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode);
+
+ //
+ // Followings support calls from Java to native WebCore
+ //
+
+ WTF::String retrieveHref(int x, int y);
+ WTF::String retrieveAnchorText(int x, int y);
+ WTF::String retrieveImageSource(int x, int y);
+ WTF::String requestLabel(WebCore::Frame* , WebCore::Node* );
+
+ // If the focus is a textfield (<input>), textarea, or contentEditable,
+ // scroll the selection on screen (if necessary).
+ void revealSelection();
+ // Create a single picture to represent the drawn DOM (used by navcache)
+ void recordPicture(SkPicture* picture);
+
+ void moveFocus(WebCore::Frame* frame, WebCore::Node* node);
+ void moveMouse(WebCore::Frame* frame, int x, int y);
+ void moveMouseIfLatest(int moveGeneration,
+ WebCore::Frame* frame, int x, int y);
+
+ // set the scroll amount that webview.java is currently showing
+ void setScrollOffset(int moveGeneration, bool sendScrollEvent, int dx, int dy);
+
+ void setGlobalBounds(int x, int y, int h, int v);
+
+ void setSizeScreenWidthAndScale(int width, int height, int screenWidth,
+ float scale, int realScreenWidth, int screenHeight, int anchorX,
+ int anchorY, bool ignoreHeight);
+
+ /**
+ * Handle key events from Java.
+ * @return Whether keyCode was handled by this class.
+ */
+ bool key(const WebCore::PlatformKeyboardEvent& event);
+
+ /**
+ * Handle (trackball) click event / dpad center press from Java.
+ * Also used when typing into an unfocused textfield, in which case 'fake'
+ * will be true.
+ */
+ void click(WebCore::Frame* frame, WebCore::Node* node, bool fake);
+
+ /**
+ * Handle touch event
+ */
+ bool handleTouchEvent(int action, Vector<int>& ids, Vector<IntPoint>& points, int actionIndex, int metaState);
+
+ /**
+ * Handle motionUp event from the UI thread (called touchUp in the
+ * WebCore thread).
+ * @param touchGeneration Generation number for touches so we can ignore
+ * touches when a newer one has been generated.
+ * @param frame Pointer to Frame containing the node that was touched.
+ * @param node Pointer to Node that was touched.
+ * @param x x-position of the touch.
+ * @param y y-position of the touch.
+ */
+ void touchUp(int touchGeneration, WebCore::Frame* frame,
+ WebCore::Node* node, int x, int y);
+
+ /**
+ * Sets the index of the label from a popup
+ */
+ void popupReply(int index);
+ void popupReply(const int* array, int count);
+
+ /**
+ * Delete text from start to end in the focused textfield.
+ * If start == end, set the selection, but perform no deletion.
+ * If there is no focus, silently fail.
+ * If start and end are out of order, swap them.
+ */
+ void deleteSelection(int start, int end, int textGeneration);
+
+ /**
+ * Set the selection of the currently focused textfield to (start, end).
+ * If start and end are out of order, swap them.
+ */
+ void setSelection(int start, int end);
+
+ /**
+ * Modifies the current selection.
+ *
+ * Note: Accessibility support.
+ *
+ * direction - The direction in which to alter the selection.
+ * granularity - The granularity of the selection modification.
+ *
+ * returns - The selected HTML as a string. This is not a well formed
+ * HTML, rather the selection annotated with the tags of all
+ * intermediary elements it crosses.
+ */
+ String modifySelection(const int direction, const int granularity);
+
+ /**
+ * Moves the selection to the given node in a given frame i.e. selects that node.
+ *
+ * Note: Accessibility support.
+ *
+ * frame - The frame in which to select is the node to be selected.
+ * node - The node to be selected.
+ *
+ * returns - The selected HTML as a string. This is not a well formed
+ * HTML, rather the selection annotated with the tags of all
+ * intermediary elements it crosses.
+ */
+ String moveSelection(WebCore::Frame* frame, WebCore::Node* node);
+
+ /**
+ * In the currently focused textfield, replace the characters from oldStart to oldEnd
+ * (if oldStart == oldEnd, this will be an insert at that position) with replace,
+ * and set the selection to (start, end).
+ */
+ void replaceTextfieldText(int oldStart,
+ int oldEnd, const WTF::String& replace, int start, int end,
+ int textGeneration);
+ void passToJs(int generation,
+ const WTF::String& , const WebCore::PlatformKeyboardEvent& );
+ /**
+ * Scroll the focused textfield to (x, y) in document space
+ */
+ void scrollFocusedTextInput(float x, int y);
+ /**
+ * Set the FocusController's active and focused states, so that
+ * the caret will draw (true) or not.
+ */
+ void setFocusControllerActive(bool active);
+
+ void saveDocumentState(WebCore::Frame* frame);
+
+ void addVisitedLink(const UChar*, int);
+
+ // TODO: I don't like this hack but I need to access the java object in
+ // order to send it as a parameter to java
+ AutoJObject getJavaObject();
+
+ // Return the parent WebView Java object associated with this
+ // WebViewCore.
+ jobject getWebViewJavaObject();
+
+ void setBackgroundColor(SkColor c);
+ void updateFrameCache();
+ void updateCacheOnNodeChange();
+ void dumpDomTree(bool);
+ void dumpRenderTree(bool);
+ void dumpNavTree();
+
+ /* We maintain a list of active plugins. The list is edited by the
+ pluginview itself. The list is used to service invals to the plugin
+ pageflipping bitmap.
+ */
+ void addPlugin(PluginWidgetAndroid*);
+ void removePlugin(PluginWidgetAndroid*);
+ // returns true if the pluginwidgit is in our active list
+ bool isPlugin(PluginWidgetAndroid*) const;
+ void invalPlugin(PluginWidgetAndroid*);
+ void drawPlugins();
+
+ // send the current screen size/zoom to all of the plugins in our list
+ void sendPluginVisibleScreen();
+
+ // send onLoad event to plugins who are descendents of the given frame
+ void notifyPluginsOnFrameLoad(const Frame*);
+
+ // gets a rect representing the current on-screen portion of the document
+ void getVisibleScreen(ANPRectI&);
+
+ // send this event to all of the plugins in our list
+ void sendPluginEvent(const ANPEvent&);
+
+ // lookup the plugin widget struct given an NPP
+ PluginWidgetAndroid* getPluginWidget(NPP npp);
+
+ // return the cursorNode if it is a plugin
+ Node* cursorNodeIsPlugin();
+
+ // Notify the Java side whether it needs to pass down the touch events
+ void needTouchEvents(bool);
+
+ void requestKeyboardWithSelection(const WebCore::Node*, int selStart, int selEnd);
+ // Notify the Java side that webkit is requesting a keyboard
+ void requestKeyboard(bool showKeyboard);
+
+ // Generates a class loader that contains classes from the plugin's apk
+ jclass getPluginClass(const WTF::String& libName, const char* className);
+
+ // Creates a full screen surface for a plugin
+ void showFullScreenPlugin(jobject webkitPlugin, NPP npp);
+
+ // Instructs the UI thread to discard the plugin's full-screen surface
+ void hideFullScreenPlugin();
+
+ // Creates a childView for the plugin but does not attach to the view hierarchy
+ jobject createSurface(jobject view);
+
+ // Adds the plugin's view (aka surface) to the view hierarchy
+ jobject addSurface(jobject view, int x, int y, int width, int height);
+
+ // Updates a Surface coordinates and dimensions for a plugin
+ void updateSurface(jobject childView, int x, int y, int width, int height);
+
+ // Destroys a SurfaceView for a plugin
+ void destroySurface(jobject childView);
+
+ // Returns the context (android.content.Context) of the WebView
+ jobject getContext();
+
+ // Manages requests to keep the screen on while the WebView is visible
+ void keepScreenOn(bool screenOn);
+
+ bool validNodeAndBounds(Frame* , Node* , const IntRect& );
+
+ // Make the rect (left, top, width, height) visible. If it can be fully
+ // fit, center it on the screen. Otherwise make sure the point specified
+ // by (left + xPercentInDoc * width, top + yPercentInDoc * height)
+ // pinned at the screen position (xPercentInView, yPercentInView).
+ void showRect(int left, int top, int width, int height, int contentWidth,
+ int contentHeight, float xPercentInDoc, float xPercentInView,
+ float yPercentInDoc, float yPercentInView);
+
+ // Scale the rect (x, y, width, height) to make it just fit and centered
+ // in the current view.
+ void centerFitRect(int x, int y, int width, int height);
+
+ // return a list of rects matching the touch point (x, y) with the slop
+ Vector<IntRect> getTouchHighlightRects(int x, int y, int slop);
+
+ // Open a file chooser for selecting a file to upload
+ void openFileChooser(PassRefPtr<WebCore::FileChooser> );
+
+ // reset the picture set to empty
+ void clearContent();
+
+ bool focusBoundsChanged();
+
+ // record the inval area, and the picture size
+ BaseLayerAndroid* recordContent(SkRegion* , SkIPoint* );
+
+ // This creates a new BaseLayerAndroid by copying the current m_content
+ // and doing a copy of the layers. The layers' content may be updated
+ // as we are calling layersSync().
+ BaseLayerAndroid* createBaseLayer();
+
+ int textWrapWidth() const { return m_textWrapWidth; }
+ float scale() const { return m_scale; }
+ float textWrapScale() const { return m_screenWidth * m_scale / m_textWrapWidth; }
+ WebCore::Frame* mainFrame() const { return m_mainFrame; }
+ void updateCursorBounds(const CachedRoot* root,
+ const CachedFrame* cachedFrame, const CachedNode* cachedNode);
+ void updateFrameCacheIfLoading();
+
+ // utility to split slow parts of the picture set
+ void splitContent(PictureSet*);
+
+ void notifyWebAppCanBeInstalled();
+
+#if ENABLE(VIDEO)
+ void enterFullscreenForVideoLayer(int layerId, const WTF::String& url);
+#endif
+
+ void setWebTextViewAutoFillable(int queryId, const string16& previewSummary);
+
+ DeviceMotionAndOrientationManager* deviceMotionAndOrientationManager() { return &m_deviceMotionAndOrientationManager; }
+
+ void listBoxRequest(WebCoreReply* reply, const uint16_t** labels,
+ size_t count, const int enabled[], size_t enabledCount,
+ bool multiple, const int selected[], size_t selectedCountOrSelection);
+ bool shouldPaintCaret() { return m_shouldPaintCaret; }
+ void setShouldPaintCaret(bool should) { m_shouldPaintCaret = should; }
+ bool isPaused() const { return m_isPaused; }
+ void setIsPaused(bool isPaused) { m_isPaused = isPaused; }
+ bool drawIsPaused() const;
+ // The actual content (without title bar) size in doc coordinate
+ int screenWidth() const { return m_screenWidth; }
+ int screenHeight() const { return m_screenHeight; }
+#if USE(CHROME_NETWORK_STACK)
+ void setWebRequestContextUserAgent();
+ void setWebRequestContextCacheMode(int mode);
+ WebRequestContext* webRequestContext();
+#endif
+ // Attempts to scroll the layer to the x,y coordinates of rect. The
+ // layer is the id of the LayerAndroid.
+ void scrollRenderLayer(int layer, const SkRect& rect);
+ // call only from webkit thread (like add/remove), return true if inst
+ // is still alive
+ static bool isInstance(WebViewCore*);
+ // if there exists at least one WebViewCore instance then we return the
+ // application context, otherwise NULL is returned.
+ static jobject getApplicationContext();
+ // Check whether a media mimeType is supported in Android media framework.
+ static bool isSupportedMediaMimeType(const WTF::String& mimeType);
+
+ // these members are shared with webview.cpp
+ static Mutex gFrameCacheMutex;
+ CachedRoot* m_frameCacheKit; // nav data being built by webcore
+ SkPicture* m_navPictureKit;
+ int m_moveGeneration; // copy of state in WebViewNative triggered by move
+ int m_touchGeneration; // copy of state in WebViewNative triggered by touch
+ int m_lastGeneration; // last action using up to date cache
+ bool m_updatedFrameCache;
+ bool m_findIsUp;
+ bool m_hasCursorBounds;
+ WebCore::IntRect m_cursorBounds;
+ WebCore::IntRect m_cursorHitBounds;
+ void* m_cursorFrame;
+ IntPoint m_cursorLocation;
+ void* m_cursorNode;
+ static Mutex gCursorBoundsMutex;
+ // These two fields go together: we use the mutex to protect access to
+ // m_buttons, so that we, and webview.cpp can look/modify the m_buttons
+ // field safely from our respective threads
+ static Mutex gButtonMutex;
+ WTF::Vector<Container> m_buttons;
+ // end of shared members
+
+ // internal functions
+ private:
+ CacheBuilder& cacheBuilder();
+ WebCore::Node* currentFocus();
+ // Compare the new set of buttons to the old one. All of the new
+ // buttons either replace our old ones or should be added to our list.
+ // Then check the old buttons to see if any are no longer needed.
+ void updateButtonList(WTF::Vector<Container>* buttons);
+ void reset(bool fromConstructor);
+ // Create a set of pictures to represent the drawn DOM, driven by
+ // the invalidated region and the time required to draw (used to draw)
+ void recordPictureSet(PictureSet* master);
+
+ friend class ListBoxReply;
+ struct JavaGlue;
+ struct JavaGlue* m_javaGlue;
+ WebCore::Frame* m_mainFrame;
+ WebCoreReply* m_popupReply;
+ WebCore::Node* m_lastFocused;
+ WebCore::IntRect m_lastFocusedBounds;
+ int m_blurringNodePointer;
+ int m_lastFocusedSelStart;
+ int m_lastFocusedSelEnd;
+ PictureSet m_content; // the set of pictures to draw
+ SkRegion m_addInval; // the accumulated inval region (not yet drawn)
+ SkRegion m_rebuildInval; // the accumulated region for rebuilt pictures
+ // Used in passToJS to avoid updating the UI text field until after the
+ // key event has been processed.
+ bool m_blockTextfieldUpdates;
+ bool m_focusBoundsChanged;
+ bool m_skipContentDraw;
+ // Passed in with key events to know when they were generated. Store it
+ // with the cache so that we can ignore stale text changes.
+ int m_textGeneration;
+ CachedRoot* m_temp;
+ SkPicture* m_tempPict;
+ int m_maxXScroll;
+ int m_maxYScroll;
+ int m_scrollOffsetX; // webview.java's current scroll in X
+ int m_scrollOffsetY; // webview.java's current scroll in Y
+ WebCore::IntPoint m_mousePos;
+ bool m_frameCacheOutOfDate;
+ bool m_progressDone;
+ int m_lastPassed;
+ int m_lastVelocity;
+ CachedHistory m_history;
+ int m_screenWidth; // width of the visible rect in document coordinates
+ int m_screenHeight;// height of the visible rect in document coordinates
+ int m_textWrapWidth;
+ float m_scale;
+ unsigned m_domtree_version;
+ bool m_check_domtree_version;
+ PageGroup* m_groupForVisitedLinks;
+ bool m_isPaused;
+ int m_cacheMode;
+ bool m_shouldPaintCaret;
+
+ SkTDArray<PluginWidgetAndroid*> m_plugins;
+ WebCore::Timer<WebViewCore> m_pluginInvalTimer;
+ void pluginInvalTimerFired(WebCore::Timer<WebViewCore>*) {
+ this->drawPlugins();
+ }
+ int m_screenOnCounter;
+
+ void doMaxScroll(CacheBuilder::Direction dir);
+ SkPicture* rebuildPicture(const SkIRect& inval);
+ void rebuildPictureSet(PictureSet* );
+ void sendNotifyProgressFinished();
+ /*
+ * Handle a mouse click, either from a touch or trackball press.
+ * @param frame Pointer to the Frame containing the node that was clicked on.
+ * @param node Pointer to the Node that was clicked on.
+ * @param fake This is a fake mouse click, used to put a textfield into focus. Do not
+ * open the IME.
+ */
+ bool handleMouseClick(WebCore::Frame*, WebCore::Node*, bool fake);
+ WebCore::HTMLAnchorElement* retrieveAnchorElement(int x, int y);
+ WebCore::HTMLElement* retrieveElement(int x, int y,
+ const WebCore::QualifiedName& );
+ WebCore::HTMLImageElement* retrieveImageElement(int x, int y);
+ // below are members responsible for accessibility support
+ String modifySelectionTextNavigationAxis(DOMSelection* selection, int direction, int granularity);
+ String modifySelectionDomNavigationAxis(DOMSelection* selection, int direction, int granularity);
+ Text* traverseNextContentTextNode(Node* fromNode, Node* toNode ,int direction);
+ bool isVisible(Node* node);
+ bool isHeading(Node* node);
+ String formatMarkup(DOMSelection* selection);
+ void selectAt(int x, int y);
+ Node* m_currentNodeDomNavigationAxis;
+ void scrollNodeIntoView(Frame* frame, Node* node);
+ bool isContentTextNode(Node* node);
+ Node* getIntermediaryInputElement(Node* fromNode, Node* toNode, int direction);
+ bool isContentInputElement(Node* node);
+ bool isDescendantOf(Node* parent, Node* node);
+ void advanceAnchorNode(DOMSelection* selection, int direction, String& markup, bool ignoreFirstNode, ExceptionCode& ec);
+ Node* getNextAnchorNode(Node* anchorNode, bool skipFirstHack, int direction);
+ Node* getImplicitBoundaryNode(Node* node, unsigned offset, int direction);
+
+#if ENABLE(TOUCH_EVENTS)
+ bool m_forwardingTouchEvents;
+#endif
+#if DEBUG_NAV_UI
+ uint32_t m_now;
+#endif
+ DeviceMotionAndOrientationManager m_deviceMotionAndOrientationManager;
+#if USE(CHROME_NETWORK_STACK)
+ scoped_refptr<WebRequestContext> m_webRequestContext;
+#endif
+
+ // called from constructor, to add this to a global list
+ static void addInstance(WebViewCore*);
+ // called from destructor, to remove this from a global list
+ static void removeInstance(WebViewCore*);
+ };
+
+} // namespace android
+
+#endif // WEBVIEWCORE_H