summaryrefslogtreecommitdiffstats
path: root/WebKit/android/jni
diff options
context:
space:
mode:
Diffstat (limited to 'WebKit/android/jni')
-rw-r--r--WebKit/android/jni/JavaBridge.cpp331
-rw-r--r--WebKit/android/jni/JavaSharedClient.cpp106
-rw-r--r--WebKit/android/jni/JavaSharedClient.h45
-rw-r--r--WebKit/android/jni/PictureSet.cpp682
-rw-r--r--WebKit/android/jni/PictureSet.h90
-rw-r--r--WebKit/android/jni/WebCoreFrameBridge.cpp1253
-rw-r--r--WebKit/android/jni/WebCoreFrameBridge.h125
-rw-r--r--WebKit/android/jni/WebCoreJni.cpp159
-rw-r--r--WebKit/android/jni/WebCoreJni.h71
-rw-r--r--WebKit/android/jni/WebCoreRefObject.h38
-rw-r--r--WebKit/android/jni/WebCoreResourceLoader.cpp359
-rw-r--r--WebKit/android/jni/WebCoreResourceLoader.h67
-rw-r--r--WebKit/android/jni/WebCoreViewBridge.h82
-rw-r--r--WebKit/android/jni/WebFrameView.cpp100
-rw-r--r--WebKit/android/jni/WebFrameView.h53
-rw-r--r--WebKit/android/jni/WebHistory.cpp879
-rw-r--r--WebKit/android/jni/WebHistory.h69
-rw-r--r--WebKit/android/jni/WebIconDatabase.cpp227
-rw-r--r--WebKit/android/jni/WebIconDatabase.h69
-rw-r--r--WebKit/android/jni/WebSettings.cpp325
-rw-r--r--WebKit/android/jni/WebViewCore.cpp2502
-rw-r--r--WebKit/android/jni/WebViewCore.h415
22 files changed, 8047 insertions, 0 deletions
diff --git a/WebKit/android/jni/JavaBridge.cpp b/WebKit/android/jni/JavaBridge.cpp
new file mode 100644
index 0000000..bd73b0a
--- /dev/null
+++ b/WebKit/android/jni/JavaBridge.cpp
@@ -0,0 +1,331 @@
+/*
+** Copyright 2006-2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define LOG_TAG "webcoreglue"
+
+#include <config.h>
+#include <wtf/Platform.h>
+
+#include "Cache.h"
+#include "CookieClient.h"
+#include "JavaSharedClient.h"
+#include "KURL.h"
+#include "NetworkStateNotifier.h"
+#include "Timer.h"
+#include "TimerClient.h"
+#include "jni_utility.h"
+#include "WebCoreJni.h"
+
+#ifdef ANDROID_INSTRUMENT
+#include "Frame.h"
+#include "SystemTime.h"
+#endif
+
+#include <jni.h>
+#include <JNIHelp.h>
+#include <SkImageRef_GlobalPool.h>
+#include <SkUtils.h>
+#include <utils/misc.h>
+
+// maximum bytes used to cache decoded images
+// (not including big images using ashmem)
+#define IMAGE_POOL_BUDGET (512 * 1024)
+
+#ifdef ANDROID_INSTRUMENT
+static uint32_t sTotalTimeUsed = 0;
+
+namespace WebCore {
+void Frame::resetSharedTimerTimeCounter()
+{
+ sTotalTimeUsed = 0;
+}
+
+void Frame::reportSharedTimerTimeCounter()
+{
+ LOG(LOG_DEBUG, "WebCore", "*-* Total native 2 (shared timer) time: %d ms\n",
+ sTotalTimeUsed);
+}
+}
+#endif
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+static jfieldID gJavaBridge_ObjectID;
+
+// ----------------------------------------------------------------------------
+
+class JavaBridge : public WebCore::TimerClient, public WebCore::CookieClient
+{
+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, WebCore::KURL const& docURL, WebCore::String const& value);
+ virtual WebCore::String cookies(WebCore::KURL const& url);
+ virtual bool cookiesEnabled();
+
+ ////////////////////////////////////////////
+
+ virtual void setSharedTimerCallback(void (*f)());
+
+ ////////////////////////////////////////////
+
+ 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 SetDeferringTimers(JNIEnv* env, jobject obj, jboolean defer);
+ static void ServiceFuncPtrQueue(JNIEnv*);
+
+private:
+ JavaVM* mJvm;
+ jobject mJavaObject;
+ jmethodID mSetSharedTimer;
+ jmethodID mStopSharedTimer;
+ jmethodID mSetCookies;
+ jmethodID mCookies;
+ jmethodID mCookiesEnabled;
+ jmethodID mSignalFuncPtrQueue;
+};
+
+static void (*sSharedTimerFiredCallback)();
+static JavaBridge* gJavaBridge;
+
+JavaBridge::JavaBridge(JNIEnv* env, jobject obj)
+{
+ mJvm = jnienv_to_javavm(env);
+ mJavaObject = adoptGlobalRef(env, 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;Ljava/lang/String;)V");
+ mCookies = env->GetMethodID(clazz, "cookies", "(Ljava/lang/String;)Ljava/lang/String;");
+ mCookiesEnabled = env->GetMethodID(clazz, "cookiesEnabled", "()Z");
+ mSignalFuncPtrQueue = env->GetMethodID(clazz, "signalServiceFuncPtrQueue", "()V");
+
+ 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");
+
+ WebCore::JavaSharedClient::SetTimerClient(this);
+ WebCore::JavaSharedClient::SetCookieClient(this);
+ gJavaBridge = this;
+}
+
+JavaBridge::~JavaBridge()
+{
+ if (mJavaObject) {
+ JNIEnv* env = javavm_to_jnienv(mJvm);
+ env->DeleteGlobalRef(mJavaObject);
+ mJavaObject = 0;
+ }
+
+ WebCore::JavaSharedClient::SetTimerClient(NULL);
+ WebCore::JavaSharedClient::SetCookieClient(NULL);
+}
+
+void
+JavaBridge::setSharedTimer(long long timemillis)
+{
+ JNIEnv* env = javavm_to_jnienv(mJvm);
+ AutoJObject obj = getRealObject(env, mJavaObject);
+ env->CallVoidMethod(obj.get(), mSetSharedTimer, timemillis);
+}
+
+void
+JavaBridge::stopSharedTimer()
+{
+ JNIEnv* env = javavm_to_jnienv(mJvm);
+ AutoJObject obj = getRealObject(env, mJavaObject);
+ env->CallVoidMethod(obj.get(), mStopSharedTimer);
+}
+
+void
+JavaBridge::setCookies(WebCore::KURL const& url, WebCore::KURL const& docUrl, WebCore::String const& value)
+{
+ JNIEnv* env = javavm_to_jnienv(mJvm);
+ const WebCore::String& urlStr = url.string();
+ jstring jUrlStr = env->NewString(urlStr.characters(), urlStr.length());
+ const WebCore::String& docUrlStr = docUrl.string();
+ jstring jDocUrlStr = env->NewString(docUrlStr.characters(), docUrlStr.length());
+ jstring jValueStr = env->NewString(value.characters(), value.length());
+
+ AutoJObject obj = getRealObject(env, mJavaObject);
+ env->CallVoidMethod(obj.get(), mSetCookies, jUrlStr, jDocUrlStr, jValueStr);
+ env->DeleteLocalRef(jUrlStr);
+ env->DeleteLocalRef(jDocUrlStr);
+ env->DeleteLocalRef(jValueStr);
+}
+
+WebCore::String
+JavaBridge::cookies(WebCore::KURL const& url)
+{
+ JNIEnv* env = javavm_to_jnienv(mJvm);
+ const WebCore::String& urlStr = url.string();
+ jstring jUrlStr = env->NewString(urlStr.characters(), urlStr.length());
+
+ AutoJObject obj = getRealObject(env, mJavaObject);
+ jstring string = (jstring)(env->CallObjectMethod(obj.get(), mCookies, jUrlStr));
+
+ WebCore::String ret = to_string(env, string);
+ env->DeleteLocalRef(jUrlStr);
+ env->DeleteLocalRef(string);
+ return ret;
+}
+
+bool
+JavaBridge::cookiesEnabled()
+{
+ JNIEnv* env = javavm_to_jnienv(mJvm);
+ AutoJObject obj = getRealObject(env, mJavaObject);
+ jboolean ret = env->CallBooleanMethod(obj.get(), mCookiesEnabled);
+ return (ret != 0);
+}
+
+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.
+ JSC::Bindings::getJNIEnv();
+ JNIEnv* env = javavm_to_jnienv(mJvm);
+ AutoJObject obj = getRealObject(env, mJavaObject);
+ env->CallVoidMethod(obj.get(), mSignalFuncPtrQueue);
+}
+
+// ----------------------------------------------------------------------------
+
+// visible to Shared
+void AndroidSignalServiceFuncPtrQueue()
+{
+ gJavaBridge->signalServiceFuncPtrQueue();
+}
+
+// ----------------------------------------------------------------------------
+
+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
+ uint32_t startTime = WebCore::get_thread_msec();
+#endif
+ SkAutoMemoryUsageProbe mup("JavaBridge::sharedTimerFired");
+ sSharedTimerFiredCallback();
+#ifdef ANDROID_INSTRUMENT
+ sTotalTimeUsed += WebCore::get_thread_msec() - startTime;
+#endif
+ }
+}
+
+void JavaBridge::SetCacheSize(JNIEnv* env, jobject obj, jint bytes)
+{
+ WebCore::cache()->setCapacities(0, bytes/2, bytes);
+ SkImageRef_GlobalPool::SetRAMBudget(IMAGE_POOL_BUDGET);
+ LOGV("--- set ImageRef budget %d\n", SkImageRef_GlobalPool::GetRAMBudget());
+}
+
+void JavaBridge::SetNetworkOnLine(JNIEnv* env, jobject obj, jboolean online)
+{
+ WebCore::networkStateNotifier().networkStateChange(online);
+}
+
+void JavaBridge::SetDeferringTimers(JNIEnv* env, jobject obj, jboolean defer)
+{
+ WebCore::setDeferringTimers(defer);
+}
+
+void JavaBridge::ServiceFuncPtrQueue(JNIEnv*)
+{
+ WebCore::JavaSharedClient::ServiceFunctionPtrQueue();
+}
+
+// ----------------------------------------------------------------------------
+
+/*
+ * 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 },
+ { "setDeferringTimers", "(Z)V",
+ (void*) JavaBridge::SetDeferringTimers },
+ { "nativeServiceFuncPtrQueue", "()V",
+ (void*) JavaBridge::ServiceFuncPtrQueue },
+};
+
+int register_javabridge(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");
+
+ return jniRegisterNativeMethods(env, "android/webkit/JWebCoreJavaBridge",
+ gWebCoreJavaBridgeMethods, NELEM(gWebCoreJavaBridgeMethods));
+}
+
+} /* namespace android */
diff --git a/WebKit/android/jni/JavaSharedClient.cpp b/WebKit/android/jni/JavaSharedClient.cpp
new file mode 100644
index 0000000..aaefd72
--- /dev/null
+++ b/WebKit/android/jni/JavaSharedClient.cpp
@@ -0,0 +1,106 @@
+/*
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include "config.h"
+#include "JavaSharedClient.h"
+#define LOG_TAG "JavaSharedClient"
+#include "utils/Log.h"
+#include "SkDeque.h"
+#include "SkThread.h"
+
+namespace android {
+ void AndroidSignalServiceFuncPtrQueue();
+}
+
+namespace WebCore {
+
+ TimerClient* JavaSharedClient::GetTimerClient()
+ {
+ //LOG_ASSERT(gTimerClient != NULL, "gTimerClient not initialized!!!");
+ return gTimerClient;
+ }
+
+ CookieClient* JavaSharedClient::GetCookieClient()
+ {
+ //LOG_ASSERT(gCookieClient != NULL, "gCookieClient not initialized!!!");
+ return gCookieClient;
+ }
+
+ void JavaSharedClient::SetTimerClient(TimerClient* client)
+ {
+ //LOG_ASSERT(gTimerClient == NULL || client == NULL, "gTimerClient already set, aborting...");
+ gTimerClient = client;
+ }
+
+ void JavaSharedClient::SetCookieClient(CookieClient* client)
+ {
+ //LOG_ASSERT(gCookieClient == NULL || client == NULL, "gCookieClient already set, aborting...");
+ gCookieClient = client;
+ }
+
+ TimerClient* JavaSharedClient::gTimerClient = NULL;
+ CookieClient* JavaSharedClient::gCookieClient = 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();
+
+ android::AndroidSignalServiceFuncPtrQueue();
+ }
+
+ void JavaSharedClient::ServiceFunctionPtrQueue()
+ {
+ for (;;) {
+ void (*proc)(void*);
+ void* payload;
+ 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 (NULL != rec) {
+ proc = rec->fProc;
+ payload = rec->fPayload;
+ gFuncPtrQ.pop_front();
+ }
+ gFuncPtrQMutex.release();
+
+ if (NULL == rec) {
+ break;
+ }
+ proc(payload);
+ }
+ }
+}
diff --git a/WebKit/android/jni/JavaSharedClient.h b/WebKit/android/jni/JavaSharedClient.h
new file mode 100644
index 0000000..470c4dd
--- /dev/null
+++ b/WebKit/android/jni/JavaSharedClient.h
@@ -0,0 +1,45 @@
+/*
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef JAVA_SHARED_CLIENT_H
+#define JAVA_SHARED_CLIENT_H
+
+namespace WebCore {
+
+ class TimerClient;
+ class CookieClient;
+
+ class JavaSharedClient
+ {
+ public:
+ static TimerClient* GetTimerClient();
+ static CookieClient* GetCookieClient();
+
+ static void SetTimerClient(TimerClient* client);
+ static void SetCookieClient(CookieClient* 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;
+ };
+}
+#endif
diff --git a/WebKit/android/jni/PictureSet.cpp b/WebKit/android/jni/PictureSet.cpp
new file mode 100644
index 0000000..e02e96a
--- /dev/null
+++ b/WebKit/android/jni/PictureSet.cpp
@@ -0,0 +1,682 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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 "SystemTime.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;
+ pictureAndBounds.mPicture->safeRef();
+ pictureAndBounds.mWroteElapsed = false;
+ mPictures.append(pictureAndBounds);
+}
+
+void PictureSet::add(const SkRegion& area, SkPicture* picture,
+ uint32_t elapsed, bool split)
+{
+ DBG_SET_LOGD("%p {%d,%d,r=%d,b=%d} elapsed=%d split=%d", this,
+ area.getBounds().fLeft, area.getBounds().fTop,
+ area.getBounds().fRight, area.getBounds().fBottom,
+ elapsed, split);
+ picture->safeRef();
+ /* 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};
+ 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) {
+ working->mPicture->safeUnref();
+ 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()
+{
+ // dump(__FUNCTION__);
+ Pictures* last = mPictures.end();
+ for (Pictures* working = mPictures.begin(); working != last; working++) {
+ working->mArea.setEmpty();
+ working->mPicture->safeUnref();
+ }
+ mPictures.clear();
+ mWidth = mHeight = 0;
+}
+
+bool PictureSet::draw(SkCanvas* canvas)
+{
+ DBG_SET_LOG("");
+ 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("first=%d last=%d", 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);
+ pathClip.computeBounds(&pathBounds, SkPath::kFast_BoundsType);
+ } else {
+ pathBounds.set(area.getBounds());
+ canvas->clipRect(pathBounds);
+ }
+ canvas->translate(pathBounds.fLeft, pathBounds.fTop);
+ canvas->save();
+ uint32_t startTime = WebCore::get_thread_msec();
+ canvas->drawPicture(*working->mPicture);
+ size_t elapsed = working->mElapsed = WebCore::get_thread_msec() - 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)", this, label, mPictures.size());
+ const Pictures* last = mPictures.end();
+ for (const Pictures* working = mPictures.begin(); working != last; working++) {
+ const SkIRect& bounds = working->mArea.getBounds();
+ MeasureStream measure;
+ if (working->mPicture != NULL)
+ working->mPicture->serialize(&measure);
+ LOGD(" [%d] {%d,%d,r=%d,b=%d} elapsed=%d split=%s"
+ " wroteElapsed=%s base=%s pictSize=%d",
+ working - mPictures.begin(),
+ bounds.fLeft, bounds.fTop, bounds.fRight, bounds.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 void commonDrawBitmap(const SkBitmap& bitmap,
+ 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
+ }
+
+ 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 (emptyPicture(working->mPicture) == false)
+ 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;
+ working->mPicture->safeUnref();
+ working->mPicture = NULL;
+ }
+ return true;
+}
+
+void PictureSet::set(const PictureSet& src)
+{
+ DBG_SET_LOG("start");
+ 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)
+{
+ mPictures[i].mPicture->safeUnref();
+ mPictures[i].mPicture = p;
+}
+
+void PictureSet::split(PictureSet* out) const
+{
+ dump(__FUNCTION__);
+ SkIRect totalBounds;
+ out->mWidth = mWidth;
+ out->mHeight = mHeight;
+ totalBounds.set(0, 0, mWidth, mHeight);
+ SkRegion* total = new SkRegion(totalBounds);
+ const Pictures* last = mPictures.end();
+ uint32_t balance = 0;
+ bool firstTime = true;
+ const Pictures* singleton = NULL;
+ int singleOut = -1;
+ for (const Pictures* working = mPictures.begin(); working != last; working++) {
+ uint32_t elapsed = working->mElapsed;
+ if (elapsed < MAX_DRAW_TIME) {
+ if (working->mSplit) {
+ total->op(working->mArea, SkRegion::kDifference_Op);
+ DBG_SET_LOGD("%p total->getBounds()={%d,%d,r=%d,b=%d", this,
+ total->getBounds().fLeft, total->getBounds().fTop,
+ total->getBounds().fRight, total->getBounds().fBottom);
+ singleOut = out->mPictures.end() - out->mPictures.begin();
+ out->add(working->mArea, working->mPicture, elapsed, true);
+ continue;
+ }
+ if (firstTime) {
+ singleton = working;
+ DBG_SET_LOGD("%p firstTime working=%p working->mArea="
+ "{%d,%d,r=%d,b=%d}", this, working,
+ working->mArea.getBounds().fLeft,
+ working->mArea.getBounds().fTop,
+ working->mArea.getBounds().fRight,
+ working->mArea.getBounds().fBottom);
+ out->add(working->mArea, working->mPicture, elapsed, false);
+ firstTime = false;
+ } else {
+ if (singleOut >= 0) {
+ Pictures& outWork = out->mPictures[singleOut];
+ DBG_SET_LOGD("%p clear singleton outWork=%p outWork->mArea="
+ "{%d,%d,r=%d,b=%d}", this, &outWork,
+ outWork.mArea.getBounds().fLeft,
+ outWork.mArea.getBounds().fTop,
+ outWork.mArea.getBounds().fRight,
+ outWork.mArea.getBounds().fBottom);
+ outWork.mArea.setEmpty();
+ outWork.mPicture->safeUnref();
+ outWork.mPicture = NULL;
+ singleOut = -1;
+ }
+ singleton = NULL;
+ }
+ 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);
+ left = right;
+ }
+ top = bottom;
+ }
+ }
+ DBG_SET_LOGD("%p w=%d h=%d total->isEmpty()=%s singleton=%p",
+ this, mWidth, mHeight, total->isEmpty() ? "true" : "false", singleton);
+ if (total->isEmpty() == false && singleton == NULL)
+ out->add(*total, NULL, balance, false);
+ delete total;
+ validate(__FUNCTION__);
+ out->dump("split-out");
+}
+
+void PictureSet::toPicture(SkPicture* result) const
+{
+ DBG_SET_LOGD("%p", this);
+ SkPicture tempPict;
+ SkAutoPictureRecord arp(&tempPict, mWidth, mHeight);
+ SkCanvas* recorder = arp.getRecordingCanvas();
+ const Pictures* last = mPictures.end();
+ for (const Pictures* working = mPictures.begin(); working != last; working++) {
+ int saved = recorder->save();
+ SkPath pathBounds;
+ working->mArea.getBoundaryPath(&pathBounds);
+ recorder->clipPath(pathBounds);
+ recorder->drawPicture(*working->mPicture);
+ recorder->restoreToCount(saved);
+ }
+ result->swap(tempPict);
+}
+
+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/WebKit/android/jni/PictureSet.h b/WebKit/android/jni/PictureSet.h
new file mode 100644
index 0000000..5ff003f
--- /dev/null
+++ b/WebKit/android/jni/PictureSet.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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);
+ const SkIRect& bounds(size_t i) {
+ 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() { return mPictures.size(); }
+ void split(PictureSet* result) const;
+ void toPicture(SkPicture* ) const;
+ bool upToDate(size_t i) { 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
+ };
+ void add(const Pictures* temp);
+ WTF::Vector<Pictures> mPictures;
+ int mHeight;
+ int mWidth;
+ };
+}
+
+#endif
diff --git a/WebKit/android/jni/WebCoreFrameBridge.cpp b/WebKit/android/jni/WebCoreFrameBridge.cpp
new file mode 100644
index 0000000..9f55134
--- /dev/null
+++ b/WebKit/android/jni/WebCoreFrameBridge.cpp
@@ -0,0 +1,1253 @@
+/*
+** Copyright 2006-2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define LOG_TAG "webcoreglue"
+
+#include <config.h>
+#include <wtf/Platform.h>
+
+#include "android_graphics.h"
+#include "Arena.h"
+#include "AtomicString.h"
+#include "Cache.h"
+#include "ChromeClientAndroid.h"
+#include "ContextMenuClientAndroid.h"
+#include "CString.h"
+#include "Document.h"
+#include "DocumentLoader.h"
+#include "DragClientAndroid.h"
+#include "EditorClientAndroid.h"
+#include "Element.h"
+#include "Font.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameLoaderClientAndroid.h"
+#include "FrameTree.h"
+#include "FrameView.h"
+#include "GCController.h"
+#include "GraphicsContext.h"
+#include "HistoryItem.h"
+#include "HTMLElement.h"
+#include "HTMLFormElement.h"
+#include "HTMLInputElement.h"
+#include "HTMLNames.h"
+#include "IconDatabase.h"
+#include "Image.h"
+#include "InspectorClientAndroid.h"
+#include "JSDOMWindow.h"
+#include <runtime/InitializeThreading.h>
+#include <runtime/JSLock.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 "ScriptController.h"
+#include "SelectionController.h"
+#include "Settings.h"
+#include "SubstituteData.h"
+#include "WebCoreFrameBridge.h"
+#include "WebCoreJni.h"
+#include "WebCoreResourceLoader.h"
+#include "WebHistory.h"
+#include "WebIconDatabase.h"
+#include "WebFrameView.h"
+#include "WebViewCore.h"
+
+#include <runtime_root.h>
+#include <runtime_object.h>
+#include <jni_utility.h>
+#include "jni.h"
+#include "jni_instance.h"
+
+#include <JNIHelp.h>
+#include <SkGraphics.h>
+#include <SkImageRef_GlobalPool.h>
+#include <utils/misc.h>
+#include <utils/AssetManager.h>
+#include <android_runtime/android_util_AssetManager.h>
+
+#ifdef ANDROID_INSTRUMENT
+#include "SystemTime.h"
+
+static uint32_t sTotalJavaTimeUsed = 0;
+static uint32_t sTotalNativeTimeUsed = 0;
+
+namespace WebCore {
+void Frame::resetFramebridgeTimeCounter()
+{
+ sTotalJavaTimeUsed = 0;
+ sTotalNativeTimeUsed = 0;
+}
+
+void Frame::reportFramebridgeTimeCounter()
+{
+ LOG(LOG_DEBUG, "WebCore", "*-* Total Java callback (frame bridge) time: %d ms\n",
+ sTotalJavaTimeUsed);
+ LOG(LOG_DEBUG, "WebCore", "*-* Total native 1 (frame bridge) time: %d ms\n",
+ sTotalNativeTimeUsed);
+}
+}
+#endif
+
+namespace android {
+
+#ifdef ANDROID_INSTRUMENT
+class TimeCounterFB {
+public:
+ TimeCounterFB(bool native = false) {
+ mNative = native;
+ mStartTime = WebCore::get_thread_msec();
+ }
+
+ ~TimeCounterFB() {
+ if (mNative)
+ sTotalNativeTimeUsed += WebCore::get_thread_msec() - mStartTime;
+ else
+ sTotalJavaTimeUsed += WebCore::get_thread_msec() - mStartTime;
+ }
+private:
+ bool mNative;
+ uint32_t mStartTime;
+};
+#endif
+
+// ----------------------------------------------------------------------------
+
+#define WEBCORE_MEMORY_CAP 15 * 1024 * 1024
+
+// ----------------------------------------------------------------------------
+
+struct WebFrame::JavaBrowserFrame
+{
+ JavaVM* mJVM;
+ jobject mObj;
+ jobject mHistoryList; // WebBackForwardList object
+ jmethodID mStartLoadingResource;
+ jmethodID mLoadStarted;
+ jmethodID mTransitionToCommitted;
+ jmethodID mLoadFinished;
+ jmethodID mReportError;
+ jmethodID mSetTitle;
+ jmethodID mWindowObjectCleared;
+ jmethodID mSetProgress;
+ jmethodID mDidReceiveIcon;
+ jmethodID mUpdateVisitedHistory;
+ jmethodID mHandleUrl;
+ jmethodID mCreateWindow;
+ jmethodID mCloseWindow;
+ jmethodID mDecidePolicyForFormResubmission;
+ jmethodID mRequestFocus;
+ jmethodID mGetRawResFilename;
+ 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->mJVM = jnienv_to_javavm(env);
+ mJavaFrame->mObj = adoptGlobalRef(env, obj);
+ mJavaFrame->mHistoryList = adoptGlobalRef(env, historyList);
+ mJavaFrame->mStartLoadingResource = env->GetMethodID(clazz, "startLoadingResource",
+ "(ILjava/lang/String;Ljava/lang/String;Ljava/util/HashMap;[BIZZ)Landroid/webkit/LoadListener;");
+ 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->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;");
+
+ LOG_ASSERT(mJavaFrame->mStartLoadingResource, "Could not find method startLoadingResource");
+ 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->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");
+
+ mUserAgent = WebCore::String();
+ mUserInitiatedClick = false;
+}
+
+WebFrame::~WebFrame()
+{
+ if (mJavaFrame->mObj) {
+ JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM);
+ env->DeleteGlobalRef(mJavaFrame->mObj);
+ env->DeleteGlobalRef(mJavaFrame->mHistoryList);
+ mJavaFrame->mObj = 0;
+ }
+ delete mJavaFrame;
+}
+
+WebFrame* WebFrame::getWebFrame(const WebCore::Frame* frame)
+{
+ WebCore::FrameLoaderClientAndroid* client =
+ static_cast<WebCore::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 = env->NewString((unsigned short *)i->first.characters(), i->first.length());
+ jstring val = env->NewString((unsigned short *)i->second.characters(), i->second.length());
+ if (key && val) {
+ env->CallObjectMethod(hashMap, put, key, val);
+ env->DeleteLocalRef(key);
+ env->DeleteLocalRef(val);
+ }
+ }
+ env->DeleteLocalRef(mapClass);
+
+ return hashMap;
+}
+
+WebCoreResourceLoader*
+WebFrame::startLoadingResource(WebCore::ResourceHandle* loader,
+ const WebCore::ResourceRequest& request,
+ bool isHighPriority, bool synchronous)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter;
+#endif
+ LOGV("::WebCore:: startLoadingResource(%p, %s)",
+ loader, request.url().string().ascii().data());
+
+ WebCore::String method = request.httpMethod();
+ WebCore::HTTPHeaderMap headers = request.httpHeaderFields();
+
+ JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM);
+ WebCore::String urlStr = request.url().string();
+ jstring jUrlStr = env->NewString(urlStr.characters(), urlStr.length());
+ jstring jMethodStr = NULL;
+ if (!method.isEmpty())
+ jMethodStr = env->NewString(method.characters(), method.length());
+ jbyteArray jPostDataStr = NULL;
+ WebCore::FormData* formdata = request.httpBody();
+ if (formdata) {
+ // 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();
+ for (size_t i = 0; i < n; ++i) {
+ const WebCore::FormDataElement& e = elements[i];
+ if (e.m_type == WebCore::FormDataElement::data) {
+ size += e.m_data.size();
+ }
+ }
+
+ // 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;
+ }
+ }
+ env->ReleaseByteArrayElements(jPostDataStr, bytes, 0);
+ }
+ }
+ }
+
+ jobject jHeaderMap = createJavaMapFromHTTPHeaders(env, headers);
+
+ // Convert the WebCore Cache Policy to a WebView Cache Policy.
+ int cacheMode = 0; // WebView.LOAD_NORMAL
+ switch (request.cachePolicy()) {
+ case WebCore::ReloadIgnoringCacheData:
+ cacheMode = 2; // WebView.LOAD_NO_CACHE
+ break;
+ case WebCore::ReturnCacheDataDontLoad:
+ cacheMode = 3; // WebView.LOAD_CACHE_ONLY
+ break;
+ case WebCore::ReturnCacheDataElseLoad:
+ cacheMode = 1; // WebView.LOAD_CACHE_ELSE_NETWORK
+ break;
+ case WebCore::UseProtocolCachePolicy:
+ default:
+ break;
+ }
+
+ LOGV("::WebCore:: startLoadingResource %s with cacheMode %d", urlStr.ascii().data(), cacheMode);
+
+
+ jobject jLoadListener =
+ env->CallObjectMethod(mJavaFrame->frame(env).get(), mJavaFrame->mStartLoadingResource,
+ (int)loader, jUrlStr, jMethodStr, jHeaderMap,
+ jPostDataStr, cacheMode, isHighPriority, synchronous);
+
+ env->DeleteLocalRef(jUrlStr);
+ env->DeleteLocalRef(jMethodStr);
+ env->DeleteLocalRef(jPostDataStr);
+ env->DeleteLocalRef(jHeaderMap);
+ if (checkException(env))
+ return NULL;
+
+ WebCoreResourceLoader* h = NULL;
+ if (jLoadListener)
+ h = new WebCoreResourceLoader(env, jLoadListener);
+ env->DeleteLocalRef(jLoadListener);
+ return h;
+}
+
+void
+WebFrame::reportError(int errorCode, const WebCore::String& description,
+ const WebCore::String& failingUrl)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter;
+#endif
+ LOGV("::WebCore:: reportError(%d, %s)", errorCode, description.ascii().data());
+ JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM);
+
+ jstring descStr = env->NewString((unsigned short*)description.characters(), description.length());
+ jstring failUrl = env->NewString((unsigned short*)failingUrl.characters(), failingUrl.length());
+ 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
+ TimeCounterFB counter;
+#endif
+ const WebCore::KURL& url = frame->loader()->activeDocumentLoader()->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::FrameLoadTypeSame ||
+ (loadType == WebCore::FrameLoadTypeRedirectWithLockedHistory &&
+ !isMainFrame))
+ return;
+
+ JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM);
+ WebCore::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 = env->NewString((unsigned short*)urlString.characters(), urlString.length());
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mLoadStarted, urlStr, favicon,
+ (int)loadType, isMainFrame);
+ checkException(env);
+ env->DeleteLocalRef(urlStr);
+ if (favicon)
+ env->DeleteLocalRef(favicon);
+}
+
+void
+WebFrame::transitionToCommitted(WebCore::Frame* frame)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter;
+#endif
+ JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM);
+ 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
+ TimeCounterFB counter;
+#endif
+ JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM);
+ WebCore::FrameLoader* loader = frame->loader();
+ const WebCore::KURL& url = loader->activeDocumentLoader()->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();
+ WebCore::String urlString(url.string());
+ jstring urlStr = env->NewString((unsigned short*)urlString.characters(), urlString.length());
+ 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
+ TimeCounterFB counter;
+#endif
+ LOGV("::WebCore:: addHistoryItem");
+ JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM);
+ WebHistory::AddItem(mJavaFrame->history(env), item);
+}
+
+void
+WebFrame::removeHistoryItem(int index)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter;
+#endif
+ LOGV("::WebCore:: removeHistoryItem at %d", index);
+ JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM);
+ WebHistory::RemoveItem(mJavaFrame->history(env), index);
+}
+
+void
+WebFrame::updateHistoryIndex(int newIndex)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter;
+#endif
+ LOGV("::WebCore:: updateHistoryIndex to %d", newIndex);
+ JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM);
+ WebHistory::UpdateHistoryIndex(mJavaFrame->history(env), newIndex);
+}
+
+void
+WebFrame::setTitle(const WebCore::String& title)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter;
+#endif
+#ifndef NDEBUG
+ LOGV("setTitle(%s)", title.ascii().data());
+#endif
+ JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM);
+ jstring jTitleStr = env->NewString((unsigned short *)title.characters(), title.length());
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mSetTitle,
+ jTitleStr);
+ checkException(env);
+ env->DeleteLocalRef(jTitleStr);
+}
+
+void
+WebFrame::windowObjectCleared(WebCore::Frame* frame)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter;
+#endif
+ LOGV("::WebCore:: windowObjectCleared");
+ JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM);
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mWindowObjectCleared, (int)frame);
+ checkException(env);
+}
+
+void
+WebFrame::setProgress(float newProgress)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter;
+#endif
+ JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM);
+ int progress = (int) (100 * newProgress);
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mSetProgress, progress);
+ checkException(env);
+}
+
+const WebCore::String
+WebFrame::userAgentForURL(const WebCore::KURL* url)
+{
+ return mUserAgent;
+}
+
+void
+WebFrame::didReceiveIcon(WebCore::Image* icon)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter;
+#endif
+ LOG_ASSERT(icon, "DidReceiveIcon called without an image!");
+ JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM);
+ jobject bitmap = webcoreImageToJavaBitmap(env, icon);
+ if (!bitmap)
+ return;
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mDidReceiveIcon, bitmap);
+ env->DeleteLocalRef(bitmap);
+ checkException(env);
+}
+
+void
+WebFrame::updateVisitedHistory(const WebCore::KURL& url, bool reload)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter;
+#endif
+ WebCore::String urlStr(url.string());
+ JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM);
+ jstring jUrlStr = env->NewString((unsigned short*)urlStr.characters(), urlStr.length());
+
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mUpdateVisitedHistory, jUrlStr, reload);
+ checkException(env);
+}
+
+bool
+WebFrame::canHandleRequest(const WebCore::ResourceRequest& request)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter;
+#endif
+ // Internal loads are ok but any request that is due to a user hitting a key
+ // should be checked.
+ bool userGesture = false;
+#ifdef ANDROID_USER_GESTURE
+ userGesture = request.userGesture();
+#endif
+ WebCore::KURL requestUrl = request.url();
+ if (!mUserInitiatedClick && !userGesture &&
+ (requestUrl.protocolIs("http") || requestUrl.protocolIs("https") ||
+ requestUrl.protocolIs("file") || requestUrl.protocolIs("about") ||
+ requestUrl.protocolIs("javascript")))
+ return true;
+ WebCore::String url(request.url().string());
+ // Empty urls should not be sent to java
+ if (url.isEmpty())
+ return true;
+ JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM);
+ jstring jUrlStr = env->NewString((unsigned short *)url.characters(), url.length());
+
+ // 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);
+ return (ret == 0);
+}
+
+WebCore::Frame*
+WebFrame::createWindow(bool dialog, bool userGesture)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter;
+#endif
+ JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM);
+ 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
+ TimeCounterFB counter;
+#endif
+ JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM);
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mRequestFocus);
+ checkException(env);
+}
+
+void
+WebFrame::closeWindow(WebViewCore* webViewCore)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter;
+#endif
+ assert(webViewCore);
+ JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM);
+ 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
+ TimeCounterFB counter;
+#endif
+ JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM);
+ PolicyFunctionWrapper* p = new PolicyFunctionWrapper;
+ p->func = func;
+ env->CallVoidMethod(mJavaFrame->frame(env).get(), mJavaFrame->mDecidePolicyForFormResubmission, p);
+}
+
+WebCore::String
+WebFrame::getRawResourceFilename(RAW_RES_ID id) const
+{
+ JNIEnv* env = javavm_to_jnienv(mJavaFrame->mJVM);
+ jstring ret = (jstring) env->CallObjectMethod(mJavaFrame->frame(env).get(),
+ mJavaFrame->mGetRawResFilename, (int)id);
+
+ return to_string(env, ret);
+}
+
+// ----------------------------------------------------------------------------
+static void CallPolicyFunction(JNIEnv* env, jobject obj, jint func, jint decision)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter(true);
+#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!");
+
+ (pFrame->loader()->*(pFunc->func))((WebCore::PolicyAction)decision);
+}
+
+static void CreateFrame(JNIEnv* env, jobject obj, jobject javaview, jobject jAssetManager, jobject historyList)
+{
+ JSC::initializeThreading();
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter(true);
+#endif
+ WebCore::ChromeClientAndroid* chromeC = new WebCore::ChromeClientAndroid;
+ WebCore::EditorClientAndroid* editorC = new WebCore::EditorClientAndroid;
+ WebCore::ContextMenuClient* contextMenuC = new WebCore::ContextMenuClientAndroid;
+ WebCore::DragClient* dragC = new WebCore::DragClientAndroid;
+ WebCore::InspectorClientAndroid* inspectorC = new WebCore::InspectorClientAndroid;
+ // Create a new page
+ WebCore::Page* page = new WebCore::Page(chromeC, contextMenuC, editorC, dragC, inspectorC);
+ /* TODO: Don't turn on PageCache until we can restore the ScrollView State.
+ * This caused bug http://b/issue?id=1202983
+ page->settings()->setUsesPageCache(true);
+ // 10 is a random number chosen because it is small enough to give the user
+ // a good back/forward page cache without allowing the page cache to get too
+ // big.
+ WebCore::pageCache()->setCapacity(10);
+ */
+ 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 chromeC and release our ownership
+ chromeC->setWebFrame(webFrame);
+ Release(webFrame);
+
+ WebCore::FrameLoaderClientAndroid* loaderC = new WebCore::FrameLoaderClientAndroid(webFrame);
+ // Create a Frame and the page holds its reference
+ WebCore::Frame* frame = WebCore::Frame::create(page, NULL, loaderC).get();
+ loaderC->setFrame(frame);
+
+ // Create a WebViewCore to access the Java WebViewCore associated with this page
+ WebViewCore* webViewCore = new WebViewCore(env, javaview, frame);
+
+ // Create a FrameView
+ WebCore::FrameView* frameView = new WebCore::FrameView(frame);
+ // Create a WebFrameView
+ WebFrameView* webFrameView = new WebFrameView(frameView, 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);
+ frameView->deref();
+
+ // Set the frame to active to turn on keyboard focus.
+ frame->init();
+ frame->selection()->setFocused(true);
+
+ // Allow local access to file:/// and substitute data
+ WebCore::FrameLoader::setLocalLoadPolicy(
+ WebCore::FrameLoader::AllowLocalLoadsForLocalAndSubstituteData);
+
+ LOGV("::WebCore:: createFrame %p", frame);
+
+ // Set the mNativeFrame field in Frame
+ SET_NATIVE_FRAME(env, obj, (int)frame);
+
+ // Setup the asset manager.
+ AssetManager* am = assetManagerForJavaObject(env, jAssetManager);
+ // Initialize our skinning classes
+ WebCore::RenderSkinAndroid::Init(am);
+}
+
+static void DestroyFrame(JNIEnv* env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter(true);
+#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);
+}
+
+static void LoadUrl(JNIEnv *env, jobject obj, jstring url)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter(true);
+#endif
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ LOG_ASSERT(pFrame, "nativeLoadUrl must take a valid frame pointer!");
+
+ WebCore::String webcoreUrl = to_string(env, url);
+ WebCore::ResourceRequest request(webcoreUrl);
+ LOGV("LoadUrl %s", webcoreUrl.latin1().data());
+ pFrame->loader()->load(request);
+}
+
+
+static void LoadData(JNIEnv *env, jobject obj, jstring baseUrl, jstring data,
+ jstring mimeType, jstring encoding, jstring failUrl)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter(true);
+#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(to_string(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,
+ to_string(env, mimeType), to_string(env, encoding),
+ WebCore::KURL(to_string(env, failUrl)));
+
+ // Perform the load
+ pFrame->loader()->load(request, substituteData);
+}
+
+static void StopLoading(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter(true);
+#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();
+}
+
+static jstring ExternalRepresentation(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter(true);
+#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
+ WebCore::String renderDump = WebCore::externalRepresentation(pFrame->contentRenderer());
+ unsigned len = renderDump.length();
+ if (!len)
+ return NULL;
+ return env->NewString(renderDump.characters(), len);
+}
+
+static jstring DocumentAsText(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter(true);
+#endif
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ LOG_ASSERT(pFrame, "android_webcore_nativeDocumentAsText must take a valid frame pointer!");
+
+ WebCore::Element *documentElement = pFrame->document()->documentElement();
+ if (!documentElement)
+ return NULL;
+ WebCore::String renderDump = ((WebCore::HTMLElement*)documentElement)->innerText();
+ renderDump.append("\n");
+ unsigned len = renderDump.length();
+ if (!len)
+ return NULL;
+ return env->NewString((unsigned short*)renderDump.characters(), len);
+}
+
+static void Reload(JNIEnv *env, jobject obj, jboolean allowStale)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter(true);
+#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)
+ loader->reloadAllowingStaleData(loader->documentLoader()->overrideEncoding());
+ else
+ loader->reload();
+}
+
+static void GoBackOrForward(JNIEnv *env, jobject obj, jint pos)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter(true);
+#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->loader()->goBackOrForward(pos);
+}
+
+static jobject StringByEvaluatingJavaScriptFromString(JNIEnv *env, jobject obj, jstring script)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter(true);
+#endif
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ LOG_ASSERT(pFrame, "stringByEvaluatingJavaScriptFromString must take a valid frame pointer!");
+
+ JSC::JSValue* r =
+ pFrame->loader()->executeScript(to_string(env, script), true);
+ WebCore::String result = WebCore::String();
+ if (r) {
+ // note: r->getString() returns a UString.
+ result = WebCore::String(r->isString() ? r->getString() :
+ r->toString(pFrame->script()->globalObject()->globalExec()));
+ }
+
+ unsigned len = result.length();
+ if (len == 0)
+ return NULL;
+ return env->NewString((unsigned short*)result.characters(), len);
+}
+
+static void AddJavascriptInterface(JNIEnv *env, jobject obj, jint nativeFramePointer,
+ jobject javascriptObj, jstring interfaceName)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter(true);
+#endif
+ WebCore::Frame* pFrame = (WebCore::Frame*)nativeFramePointer;
+ LOG_ASSERT(pFrame, "nativeAddJavascriptInterface must take a valid frame pointer!");
+
+ JavaVM* vm;
+ env->GetJavaVM(&vm);
+ LOGV("::WebCore:: addJSInterface: %p", pFrame);
+
+ // Copied from qwebframe.cpp
+ JSC::JSLock lock(false);
+ WebCore::JSDOMWindow *window = WebCore::toJSDOMWindow(pFrame);
+ JSC::Bindings::RootObject *root = pFrame->script()->bindingRootObject();
+ if (window) {
+ JSC::Bindings::setJavaVM(vm);
+ // Add the binding to JS environment
+ JSC::ExecState* exec = window->globalExec();
+ JSC::JSObject *addedObject = JSC::Bindings::Instance::createRuntimeObject(
+ exec, JSC::Bindings::JavaInstance::create(javascriptObj, root));
+ // Add the binding name to the window's table of child objects.
+ JSC::PutPropertySlot slot;
+ window->put(exec,
+ JSC::Identifier(exec, to_string(env, interfaceName)),
+ addedObject, slot);
+ }
+}
+
+static void SetCacheDisabled(JNIEnv *env, jobject obj, jboolean disabled)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter(true);
+#endif
+ WebCore::cache()->setDisabled(disabled);
+}
+
+static jboolean CacheDisabled(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter(true);
+#endif
+ return WebCore::cache()->disabled();
+}
+
+static void ClearCache(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter(true);
+#endif
+ 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);
+ }
+ // force JavaScript to GC when clear cache
+ WebCore::gcController().garbageCollectSoon();
+ // clear image cache
+ SkImageRef_GlobalPool::SetRAMUsed(0);
+}
+
+static jboolean DocumentHasImages(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter(true);
+#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
+ TimeCounterFB counter(true);
+#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();
+ while (node && !found) {
+ WTF::Vector<WebCore::HTMLFormControlElement*> elements =
+ ((WebCore::HTMLFormElement*)node)->formElements;
+ size_t size = elements.size();
+ for (size_t i = 0; i< size && !found; i++) {
+ WebCore::HTMLFormControlElement* e = elements[i];
+ if (e->hasLocalName(WebCore::HTMLNames::inputTag)) {
+ if (((WebCore::HTMLInputElement*)e)->inputType() ==
+ WebCore::HTMLInputElement::PASSWORD)
+ found = true;
+ }
+ }
+ node = form->nextItem();
+ }
+ return found;
+}
+
+static jobjectArray GetUsernamePassword(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter(true);
+#endif
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ LOG_ASSERT(pFrame, "GetUsernamePassword must take a valid frame pointer!");
+ jobjectArray strArray = NULL;
+
+ WebCore::String username, password;
+ bool found = false;
+ WTF::PassRefPtr<WebCore::HTMLCollection> form = pFrame->document()->forms();
+ WebCore::Node* node = form->firstItem();
+ while (node && !found) {
+ WTF::Vector<WebCore::HTMLFormControlElement*> elements =
+ ((WebCore::HTMLFormElement*)node)->formElements;
+ size_t size = elements.size();
+ for (size_t i = 0; i< size && !found; i++) {
+ WebCore::HTMLFormControlElement* e = elements[i];
+ if (e->hasLocalName(WebCore::HTMLNames::inputTag)) {
+ WebCore::HTMLInputElement* input = (WebCore::HTMLInputElement*)e;
+ if (input->autoComplete() == false)
+ continue;
+ if (input->inputType() == WebCore::HTMLInputElement::PASSWORD)
+ password = input->value();
+ else if (input->inputType() == WebCore::HTMLInputElement::TEXT)
+ username = input->value();
+ if (!username.isNull() && !password.isNull())
+ found = true;
+ }
+ }
+ node = form->nextItem();
+ }
+ if (found) {
+ jclass stringClass = env->FindClass("java/lang/String");
+ strArray = env->NewObjectArray(2, stringClass, NULL);
+ env->SetObjectArrayElement(strArray, 0, env->NewString((unsigned short *)
+ username.characters(), username.length()));
+ env->SetObjectArrayElement(strArray, 1, env->NewString((unsigned short *)
+ password.characters(), password.length()));
+ }
+ return strArray;
+}
+
+static void SetUsernamePassword(JNIEnv *env, jobject obj,
+ jstring username, jstring password)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter(true);
+#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) {
+ WTF::Vector<WebCore::HTMLFormControlElement*> elements =
+ ((WebCore::HTMLFormElement*)node)->formElements;
+ size_t size = elements.size();
+ for (size_t i = 0; i< size && !found; i++) {
+ WebCore::HTMLFormControlElement* e = elements[i];
+ if (e->hasLocalName(WebCore::HTMLNames::inputTag)) {
+ WebCore::HTMLInputElement* input = (WebCore::HTMLInputElement*)e;
+ if (input->autoComplete() == false)
+ continue;
+ if (input->inputType() == WebCore::HTMLInputElement::PASSWORD)
+ passwordEle = input;
+ else if (input->inputType() == WebCore::HTMLInputElement::TEXT)
+ usernameEle = input;
+ if (usernameEle != NULL && passwordEle != NULL)
+ found = true;
+ }
+ }
+ node = form->nextItem();
+ }
+ if (found) {
+ usernameEle->setValue(to_string(env, username));
+ passwordEle->setValue(to_string(env, password));
+ }
+}
+
+static jobject GetFormTextData(JNIEnv *env, jobject obj)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterFB counter(true);
+#endif
+ WebCore::Frame* pFrame = GET_NATIVE_FRAME(env, obj);
+ LOG_ASSERT(pFrame, "GetFormTextData must take a valid frame pointer!");
+ jobject hashMap = NULL;
+
+ WTF::PassRefPtr<WebCore::HTMLCollection> collection = pFrame->document()->forms();
+ if (collection->length() > 0) {
+ 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");
+ 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");
+
+ static const WebCore::AtomicString text("text");
+ static const WebCore::AtomicString off("off");
+
+ WebCore::HTMLFormElement* form;
+ WebCore::HTMLInputElement* input;
+ for (WebCore::Node* node = collection->firstItem(); node; node = collection->nextItem()) {
+ form = static_cast<WebCore::HTMLFormElement*>(node);
+ if (form->autoComplete()) {
+ WTF::Vector<WebCore::HTMLFormControlElement*> elements = form->formElements;
+ size_t size = elements.size();
+ for (size_t i = 0; i < size; i++) {
+ WebCore::HTMLFormControlElement* e = elements[i];
+ if (e->type() == text) {
+ if (e->hasAttribute(WebCore::HTMLNames::autocompleteAttr)) {
+ const WebCore::AtomicString& attr = e->getAttribute(WebCore::HTMLNames::autocompleteAttr);
+ if (attr == off)
+ continue;
+ }
+ input = (WebCore::HTMLInputElement*) e;
+ WebCore::String value = input->value();
+ int len = value.length();
+ if (len) {
+ const WebCore::AtomicString& name = input->name();
+ jstring key = env->NewString((jchar *)name.characters(), name.length());
+ jstring val = env->NewString((jchar *)value.characters(), len);
+ LOG_ASSERT(key && val, "name or value not set");
+ env->CallObjectMethod(hashMap, put, key, val);
+ env->DeleteLocalRef(key);
+ env->DeleteLocalRef(val);
+ }
+ }
+ }
+ }
+ }
+ env->DeleteLocalRef(mapClass);
+
+ }
+ return hashMap;
+}
+
+// ----------------------------------------------------------------------------
+
+/*
+ * 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 },
+ { "stopLoading", "()V",
+ (void*) StopLoading },
+ { "nativeLoadUrl", "(Ljava/lang/String;)V",
+ (void*) LoadUrl },
+ { "nativeLoadData", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
+ (void*) LoadData },
+ { "externalRepresentation", "()Ljava/lang/String;",
+ (void*) ExternalRepresentation },
+ { "documentAsText", "()Ljava/lang/String;",
+ (void*) DocumentAsText },
+ { "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 },
+ { "getFormTextData", "()Ljava/util/HashMap;",
+ (void*) GetFormTextData }
+};
+
+int register_webframe(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");
+
+ return jniRegisterNativeMethods(env, "android/webkit/BrowserFrame",
+ gBrowserFrameNativeMethods, NELEM(gBrowserFrameNativeMethods));
+}
+
+} /* namespace android */
+
diff --git a/WebKit/android/jni/WebCoreFrameBridge.h b/WebKit/android/jni/WebCoreFrameBridge.h
new file mode 100644
index 0000000..6dcae0f
--- /dev/null
+++ b/WebKit/android/jni/WebCoreFrameBridge.h
@@ -0,0 +1,125 @@
+/*
+** Copyright 2006-2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+// TODO: change name to WebFrame.h
+
+#ifndef WEBFRAME_H
+#define WEBFRAME_H
+
+#include "FrameLoaderClient.h"
+#include "PlatformString.h"
+#include "WebCoreRefObject.h"
+#include <jni.h>
+
+namespace WebCore {
+ class HistoryItem;
+ class Image;
+ class Page;
+ class RenderPart;
+ class ResourceHandle;
+ class ResourceRequest;
+}
+
+namespace android {
+
+class WebCoreResourceLoader;
+class WebViewCore;
+
+// one instance of WebFrame per Page for calling into Java's BrowserFrame
+class WebFrame : public WebCoreRefObject {
+ public:
+ // these ids need to be in sync with the constants in BrowserFrame.java
+ enum RAW_RES_ID {
+ NODOMAIN = 1,
+ LOADERROR,
+ };
+ WebFrame(JNIEnv* env, jobject obj, jobject historyList, WebCore::Page* page);
+ ~WebFrame();
+
+ // helper function
+ static WebFrame* getWebFrame(const WebCore::Frame* frame);
+
+ WebCoreResourceLoader* startLoadingResource(WebCore::ResourceHandle*,
+ const WebCore::ResourceRequest& request,
+ bool isHighPriority,
+ bool synchronous);
+
+ void reportError(int errorCode, const WebCore::String& description,
+ const WebCore::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 WebCore::String& title);
+
+ void windowObjectCleared(WebCore::Frame* frame);
+
+ void setProgress(float newProgress);
+
+ const WebCore::String userAgentForURL(const WebCore::KURL* url);
+
+ void didReceiveIcon(WebCore::Image* icon);
+
+ void updateVisitedHistory(const WebCore::KURL& url, bool reload);
+
+ 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(WebCore::String userAgent) { mUserAgent = userAgent; }
+
+ WebCore::String getRawResourceFilename(RAW_RES_ID) const;
+
+ /**
+ * When the user initiates a click (via trackball, enter-press, or touch),
+ * we set mUserInitiatedClick 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.
+ * We also check it to determine whether or not to allow webkit to request
+ * a scroll. If it was user initated, the scroll is allowed.
+ */
+ void setUserInitiatedClick(bool userInitiatedClick) { mUserInitiatedClick = userInitiatedClick; }
+
+ bool userInitiatedClick() { return mUserInitiatedClick; }
+
+ WebCore::Page* page() const { return mPage; }
+
+ private:
+ struct JavaBrowserFrame;
+ JavaBrowserFrame* mJavaFrame;
+ WebCore::Page* mPage;
+ WebCore::String mUserAgent;
+ bool mUserInitiatedClick;
+};
+
+} // namespace android
+
+#endif // WEBFRAME_H
diff --git a/WebKit/android/jni/WebCoreJni.cpp b/WebKit/android/jni/WebCoreJni.cpp
new file mode 100644
index 0000000..85fa720
--- /dev/null
+++ b/WebKit/android/jni/WebCoreJni.cpp
@@ -0,0 +1,159 @@
+/*
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+#define LOG_TAG "webcoreglue"
+
+#include "config.h"
+
+#include "WebCoreJni.h"
+#include "jni_utility.h"
+#include <jni.h>
+#include <utils/Log.h>
+
+namespace android {
+
+extern int register_webframe(JNIEnv*);
+extern int register_javabridge(JNIEnv*);
+extern int register_resource_loader(JNIEnv*);
+extern int register_webviewcore(JNIEnv*);
+extern int register_webhistory(JNIEnv*);
+extern int register_webicondatabase(JNIEnv*);
+extern int register_websettings(JNIEnv*);
+extern int register_webview(JNIEnv*);
+
+// Class, constructor, and get method on WeakReference
+jclass gWeakRefClass;
+jmethodID gWeakRefInit;
+jmethodID gWeakRefGet;
+
+jobject adoptGlobalRef(JNIEnv* env, jobject obj)
+{
+ // Create a WeakReference object
+ jobject ref = env->NewObject(gWeakRefClass, gWeakRefInit, obj);
+ // Increment the ref count of the WeakReference
+ ref = env->NewGlobalRef(ref);
+ return ref;
+}
+
+AutoJObject getRealObject(JNIEnv* env, jobject obj)
+{
+ jobject real = env->CallObjectMethod(obj, gWeakRefGet);
+ if (!real)
+ LOGE("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.
+WebCore::String to_string(JNIEnv* env, jstring str)
+{
+ if (!str || !env)
+ return WebCore::String();
+ const jchar* s = env->GetStringChars(str, NULL);
+ if (!s)
+ return WebCore::String();
+ WebCore::String ret(s, env->GetStringLength(str));
+ env->ReleaseStringChars(str, s);
+ checkException(env);
+ return ret;
+}
+
+JavaVM* jnienv_to_javavm(JNIEnv* env)
+{
+ JavaVM* vm;
+ return env->GetJavaVM(&vm) >= 0 ? vm : NULL;
+}
+
+JNIEnv* javavm_to_jnienv(JavaVM* vm)
+{
+ JNIEnv* env;
+ return vm->GetEnv((void **)&env, JNI_VERSION_1_4) >= 0 ? env : NULL;
+}
+
+}
+
+struct RegistrationMethod {
+ const char* name;
+ int (*func)(JNIEnv*);
+};
+
+static RegistrationMethod gWebCoreRegMethods[] = {
+ { "JavaBridge", android::register_javabridge },
+ { "WebFrame", android::register_webframe },
+ { "WebCoreResourceLoader", android::register_resource_loader },
+ { "WebViewCore", android::register_webviewcore },
+ { "WebHistory", android::register_webhistory },
+ { "WebIconDatabase", android::register_webicondatabase },
+ { "WebSettings", android::register_websettings },
+ { "WebView", android::register_webview }
+};
+
+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!");
+
+ // Instantiate the WeakReference fields.
+ android::gWeakRefClass = env->FindClass("java/lang/ref/WeakReference");
+ LOG_ASSERT(android::gWeakRefClass, "Could not find WeakReference");
+ android::gWeakRefInit = env->GetMethodID(android::gWeakRefClass,
+ "<init>", "(Ljava/lang/Object;)V");
+ LOG_ASSERT(android::gWeakRefInit,
+ "Could not find constructor for WeakReference");
+ android::gWeakRefGet = env->GetMethodID(android::gWeakRefClass, "get",
+ "()Ljava/lang/Object;");
+ LOG_ASSERT(android::gWeakRefInit,
+ "Could not find get method for WeakReference");
+
+ 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;
+}
diff --git a/WebKit/android/jni/WebCoreJni.h b/WebKit/android/jni/WebCoreJni.h
new file mode 100644
index 0000000..9acc2f4
--- /dev/null
+++ b/WebKit/android/jni/WebCoreJni.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_WEBKIT_WEBCOREJNI_H
+#define ANDROID_WEBKIT_WEBCOREJNI_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() {
+ if (m_obj)
+ m_env->DeleteLocalRef(m_obj);
+ }
+ jobject get() const {
+ return m_obj;
+ }
+ JNIEnv* env() const {
+ return m_env;
+ }
+private:
+ 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 WeakReference returned as an
+// AutoJObject.
+AutoJObject getRealObject(JNIEnv*, jobject);
+
+// Convert the given jobject to a WeakReference and create a new global
+// reference to that WeakReference.
+jobject adoptGlobalRef(JNIEnv*, jobject);
+
+// Helper method for check java exceptions. Returns true if an exception
+// occurred and logs the exception.
+bool checkException(JNIEnv* env);
+
+// Get the JavaVM pointer for the given JNIEnv pointer
+JavaVM* jnienv_to_javavm(JNIEnv* env);
+
+// Get the JNIEnv pointer for the given JavaVM pointer
+JNIEnv* javavm_to_jnienv(JavaVM* vm);
+
+// Create a WebCore::String object from a jstring object.
+WebCore::String to_string(JNIEnv* env, jstring str);
+
+}
+
+#endif
diff --git a/WebKit/android/jni/WebCoreRefObject.h b/WebKit/android/jni/WebCoreRefObject.h
new file mode 100644
index 0000000..7e96191
--- /dev/null
+++ b/WebKit/android/jni/WebCoreRefObject.h
@@ -0,0 +1,38 @@
+/*
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#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/WebKit/android/jni/WebCoreResourceLoader.cpp b/WebKit/android/jni/WebCoreResourceLoader.cpp
new file mode 100644
index 0000000..2e349e8
--- /dev/null
+++ b/WebKit/android/jni/WebCoreResourceLoader.cpp
@@ -0,0 +1,359 @@
+/*
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define LOG_TAG "webcoreglue"
+
+#include <config.h>
+#include <wtf/Platform.h>
+
+#include "jni_utility.h"
+#include "WebCoreResourceLoader.h"
+#include "SkUtils.h"
+
+#include "CString.h"
+#include "ResourceError.h"
+#include "ResourceHandle.h"
+#include "ResourceHandleClient.h"
+#include "ResourceHandleInternal.h"
+#include "ResourceResponse.h"
+#include "WebCoreJni.h"
+
+#ifdef ANDROID_INSTRUMENT
+#include "Frame.h"
+#include "SystemTime.h"
+#endif
+
+#include <utils/misc.h>
+#include <JNIHelp.h>
+#include <SkTypes.h>
+#include <stdlib.h>
+
+#ifdef ANDROID_INSTRUMENT
+static uint32_t sTotalTimeUsed = 0;
+
+namespace WebCore {
+void Frame::resetResourceLoadTimeCounter()
+{
+ sTotalTimeUsed = 0;
+}
+
+void Frame::reportResourceLoadTimeCounter()
+{
+ LOG(LOG_DEBUG, "WebCore", "*-* Total native 3 (resource load) time: %d ms\n",
+ sTotalTimeUsed);
+}
+}
+#endif
+
+namespace android {
+
+#ifdef ANDROID_INSTRUMENT
+class TimeCounterRC {
+public:
+ TimeCounterRC() {
+ mStartTime = WebCore::get_thread_msec();
+ }
+
+ ~TimeCounterRC() {
+ sTotalTimeUsed += WebCore::get_thread_msec() - mStartTime;
+ }
+
+private:
+ uint32_t mStartTime;
+};
+#endif
+
+// ----------------------------------------------------------------------------
+
+static struct resourceloader_t {
+ jfieldID mObject;
+ jmethodID mCancelMethodID;
+ jmethodID mDownloadFileMethodID;
+ jmethodID mWillLoadFromCacheMethodID;
+} 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
+
+WebCoreResourceLoader::WebCoreResourceLoader(JNIEnv *env, jobject jLoadListener)
+{
+ mJvm = jnienv_to_javavm(env);
+ mJLoader = env->NewGlobalRef(jLoadListener);
+}
+
+WebCoreResourceLoader::~WebCoreResourceLoader()
+{
+ JNIEnv* env = javavm_to_jnienv(mJvm);
+ SET_NATIVE_HANDLE(env, mJLoader, 0);
+ env->DeleteGlobalRef(mJLoader);
+ mJLoader = 0;
+}
+
+void WebCoreResourceLoader::cancel()
+{
+ JNIEnv* env = javavm_to_jnienv(mJvm);
+ env->CallVoidMethod(mJLoader, gResourceLoader.mCancelMethodID);
+ checkException(env);
+}
+
+void WebCoreResourceLoader::downloadFile()
+{
+ JNIEnv* env = javavm_to_jnienv(mJvm);
+ env->CallVoidMethod(mJLoader, gResourceLoader.mDownloadFileMethodID);
+ 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)
+{
+ JNIEnv* env = javavm_to_jnienv(JSC::Bindings::getJavaVM());
+ WebCore::String urlStr = url.string();
+ jstring jUrlStr = env->NewString(urlStr.characters(), urlStr.length());
+ jclass resourceLoader = env->FindClass("android/webkit/LoadListener");
+ bool val = env->CallStaticBooleanMethod(resourceLoader,
+ gResourceLoader.mWillLoadFromCacheMethodID, jUrlStr);
+ checkException(env);
+ env->DeleteLocalRef(jUrlStr);
+
+ return val;
+}
+
+// ----------------------------------------------------------------------------
+void WebCoreResourceLoader::SetResponseHeader(JNIEnv* env, jobject obj, jint nativeResponse, jstring key, jstring val)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterRC counter;
+#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) {
+ WebCore::String valStr = to_string(env, val);
+ if (!valStr.isEmpty())
+ response->setHTTPHeaderField(to_string(env, key), valStr);
+ }
+}
+
+jint WebCoreResourceLoader::CreateResponse(JNIEnv* env, jobject obj, jstring url, jint statusCode,
+ jstring statusText, jstring mimeType, jlong expectedLength,
+ jstring encoding, jlong expireTime)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterRC counter;
+#endif
+ LOG_ASSERT(url, "Must have a url in the response!");
+ WebCore::KURL kurl(to_string(env, url));
+ WebCore::String encodingStr;
+ WebCore::String mimeTypeStr;
+ if (mimeType) {
+ mimeTypeStr = to_string(env, mimeType);
+ LOGV("Response setMIMEType: %s", mimeTypeStr.latin1().data());
+ }
+ if (encoding) {
+ encodingStr = to_string(env, encoding);
+ LOGV("Response setTextEncodingName: %s", encodingStr.latin1().data());
+ }
+ WebCore::ResourceResponse* response = new WebCore::ResourceResponse(
+ kurl, mimeTypeStr, (long long)expectedLength,
+ encodingStr, WebCore::String());
+ response->setHTTPStatusCode(statusCode);
+ if (statusText) {
+ WebCore::String status = to_string(env, statusText);
+ response->setHTTPStatusText(status);
+ LOGV("Response setStatusText: %s", status.latin1().data());
+ }
+ // FIXME: This assumes that time_t is a long and that long is the same size as int.
+ if ((unsigned long)expireTime > INT_MAX)
+ expireTime = INT_MAX;
+ response->setExpirationDate((time_t)expireTime);
+ return (int)response;
+}
+
+void WebCoreResourceLoader::ReceivedResponse(JNIEnv* env, jobject obj, jint nativeResponse)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterRC counter;
+#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
+ TimeCounterRC counter;
+#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
+ TimeCounterRC counter;
+#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);
+}
+
+jstring WebCoreResourceLoader::RedirectedToUrl(JNIEnv* env, jobject obj,
+ jstring baseUrl, jstring redirectTo, jint nativeResponse)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterRC counter;
+#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->request();
+ WebCore::KURL url(WebCore::KURL(to_string(env, baseUrl)),
+ to_string(env, redirectTo));
+ r.setURL(url);
+ if (r.httpMethod() == "POST") {
+ r.setHTTPMethod("GET");
+ r.clearHTTPReferrer();
+ r.setHTTPBody(0);
+ r.setHTTPContentType("");
+ }
+ 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;
+ }
+ handle->client()->willSendRequest(handle, r, *response);
+ delete response;
+ WebCore::String s = url.string();
+ return env->NewString((unsigned short*)s.characters(), s.length());
+}
+
+void WebCoreResourceLoader::Error(JNIEnv* env, jobject obj, jint id, jstring description,
+ jstring failingUrl)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterRC counter;
+#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,
+ to_string(env, failingUrl), to_string(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;J)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 register_resource_loader(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.mWillLoadFromCacheMethodID =
+ env->GetStaticMethodID(resourceLoader, "willLoadFromCache", "(Ljava/lang/String;)Z");
+ LOG_FATAL_IF(gResourceLoader.mWillLoadFromCacheMethodID == NULL,
+ "Could not find static method willLoadFromCache on LoadListener");
+
+ return jniRegisterNativeMethods(env, "android/webkit/LoadListener",
+ gResourceloaderMethods, NELEM(gResourceloaderMethods));
+}
+
+} /* namespace android */
diff --git a/WebKit/android/jni/WebCoreResourceLoader.h b/WebKit/android/jni/WebCoreResourceLoader.h
new file mode 100644
index 0000000..e3b3cc7
--- /dev/null
+++ b/WebKit/android/jni/WebCoreResourceLoader.h
@@ -0,0 +1,67 @@
+/* //device/libs/android_runtime/android_webcore_resource_loader.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_WEBKIT_RESOURCELOADLISTENER_H
+#define ANDROID_WEBKIT_RESOURCELOADLISTENER_H
+
+#include "KURL.h"
+
+#include "WebCoreRefObject.h"
+#include <jni.h>
+
+namespace android {
+
+class WebCoreResourceLoader : public WebCoreRefObject
+{
+public:
+ WebCoreResourceLoader(JNIEnv *env, jobject jLoadListener);
+ virtual ~WebCoreResourceLoader();
+
+ /**
+ * Call to java to cancel the current load.
+ */
+ void cancel();
+
+ /**
+ * Call to java to download the current load rather than feed it
+ * back to WebCore
+ */
+ void downloadFile();
+
+ /**
+ * Call to java to find out if this URL is in the cache
+ */
+ static bool willLoadFromCache(const WebCore::KURL& url);
+
+ // Native jni functions
+ static void SetResponseHeader(JNIEnv*, jobject, jint, jstring, jstring);
+ static jint CreateResponse(JNIEnv*, jobject, jstring, jint, jstring,
+ jstring, jlong, jstring, jlong);
+ 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);
+
+private:
+ JavaVM* mJvm;
+ jobject mJLoader;
+};
+
+} // end namespace android
+
+#endif
diff --git a/WebKit/android/jni/WebCoreViewBridge.h b/WebKit/android/jni/WebCoreViewBridge.h
new file mode 100644
index 0000000..361e9c4
--- /dev/null
+++ b/WebKit/android/jni/WebCoreViewBridge.h
@@ -0,0 +1,82 @@
+/*
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#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() :
+ mBounds(0,0,0,0),
+ m_windowBounds(0,0,0,0)
+ {}
+ virtual ~WebCoreViewBridge() {}
+
+ virtual void draw(WebCore::GraphicsContext* ctx,
+ const WebCore::IntRect& rect) = 0;
+
+ const WebCore::IntRect& getBounds() const
+ {
+ return mBounds;
+ }
+
+ const WebCore::IntRect& getWindowBounds() const
+ {
+ return m_windowBounds;
+ }
+
+ void setSize(int w, int h)
+ {
+ mBounds.setWidth(w);
+ mBounds.setHeight(h);
+ }
+
+ void setLocation(int x, int y)
+ {
+ mBounds.setX(x);
+ mBounds.setY(y);
+ }
+
+ void setWindowBounds(int x, int y, int h, int v)
+ {
+ m_windowBounds = WebCore::IntRect(x, y, h, v);
+ }
+
+ int width() const { return mBounds.width(); }
+ int height() const { return mBounds.height(); }
+ int locX() const { return mBounds.x(); }
+ int locY() const { return mBounds.y(); }
+
+ virtual bool forFrameView() const { return false; }
+ virtual bool forPluginView() const { return false; }
+
+private:
+ WebCore::IntRect mBounds;
+ WebCore::IntRect m_windowBounds;
+};
+
+#endif // WEBCORE_VIEW_BRIDGE_H
diff --git a/WebKit/android/jni/WebFrameView.cpp b/WebKit/android/jni/WebFrameView.cpp
new file mode 100644
index 0000000..3310c51
--- /dev/null
+++ b/WebKit/android/jni/WebFrameView.cpp
@@ -0,0 +1,100 @@
+/*
+ *
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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.
+ SkRect r;
+ android_setrect(&r, transRect);
+
+ canvas->save();
+ canvas->translate(SkIntToScalar(bounds.x()), SkIntToScalar(bounds.y()));
+ canvas->clipRect(r);
+ }
+ 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/WebKit/android/jni/WebFrameView.h b/WebKit/android/jni/WebFrameView.h
new file mode 100644
index 0000000..350a301
--- /dev/null
+++ b/WebKit/android/jni/WebFrameView.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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);
+
+ virtual bool forFrameView() const { return true; }
+
+ private:
+ WebCore::FrameView* mFrameView;
+ WebViewCore* mWebViewCore;
+ };
+
+} // namespace android
+
+#endif // WEB_FRAMEVIEW_H
diff --git a/WebKit/android/jni/WebHistory.cpp b/WebKit/android/jni/WebHistory.cpp
new file mode 100644
index 0000000..203869e
--- /dev/null
+++ b/WebKit/android/jni/WebHistory.cpp
@@ -0,0 +1,879 @@
+/*
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define LOG_TAG "webhistory"
+
+#include <config.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/Platform.h>
+
+#include "WebHistory.h"
+
+#include "BackForwardList.h"
+#include "CString.h"
+#include "DocumentLoader.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameLoaderClientAndroid.h"
+#include "FrameTree.h"
+#include "HistoryItem.h"
+#include "Page.h"
+#include "TextEncoding.h"
+#include "WebCoreFrameBridge.h"
+#include "WebCoreJni.h"
+
+#include <JNIHelp.h>
+#include <SkUtils.h>
+#include <utils/misc.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;
+ jfieldID mCurrentIndex;
+} 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::BackForwardList* list = 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(NULL);
+ // 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()->setCurrentHistoryItem(current);
+ 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->childItemWithName(child->tree()->name());
+ child->loader()->setCurrentHistoryItem(item);
+ // Append the first child to the queue if it exists.
+ if (WebCore::Frame* f = child->tree()->firstChild())
+ frameQueue.append(f);
+ 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()->currentHistoryItem();
+ }
+ }
+ }
+}
+
+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;
+
+ // Set the current index in the list.
+ WebCore::BackForwardList* list = pFrame->page()->backForwardList();
+ WebCore::HistoryItem* currentItem = list->entries()[index].get();
+ list->goToItem(currentItem);
+
+ // Update the current and previous history item.
+ WebCore::FrameLoader* loader = pFrame->loader();
+ loader->setCurrentHistoryItem(currentItem);
+ loader->setPreviousHistoryItem(list->backItem());
+
+ // Update the request with the current item's info.
+ WebCore::ResourceRequest& request = loader->documentLoader()->request();
+ request.setURL(currentItem->url());
+ request.setMainDocumentURL(currentItem->url());
+ // Reload the current page
+ loader->reloadAllowingStaleData(loader->documentLoader()->overrideEncoding());
+}
+
+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();
+#ifdef ANDROID_HISTORY_CLIENT
+ RefPtr<WebHistoryItem> bridge = new WebHistoryItem(env, obj, newItem.get());
+ newItem->setBridge(bridge.get());
+#endif
+ // 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);
+#ifdef ANDROID_HISTORY_CLIENT
+ bridge->setActive();
+#endif
+
+ // Add the new item to the back/forward list.
+ WebCore::Frame* pFrame = (WebCore::Frame*)frame;
+ pFrame->page()->backForwardList()->addItem(newItem);
+
+#ifdef ANDROID_HISTORY_CLIENT
+ // Update the item.
+ bridge->updateHistoryItem(newItem.get());
+#endif
+}
+
+// 7 empty strings + no document state + children count = 9 unsigned values
+// 1 char for isTargetItem
+// ANDROID_HISTORY_CLIENT adds 1 int for scale.
+#ifdef ANDROID_HISTORY_CLIENT
+#define HISTORY_MIN_SIZE ((int)(sizeof(unsigned) * 10 + sizeof(char)))
+#else
+#define HISTORY_MIN_SIZE ((int)(sizeof(unsigned) * 9 + sizeof(char)))
+#endif
+
+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.
+#ifdef ANDROID_HISTORY_CLIENT
+ LOG_ASSERT(item->bridge(), "Why don't we have a bridge object here?");
+#endif
+ 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.
+ jbyte* bytes = env->GetByteArrayElements(b, NULL);
+ memcpy(bytes, v.data(), v.size());
+ env->ReleaseByteArrayElements(b, bytes, 0);
+ return b;
+}
+
+WebHistoryItem::WebHistoryItem(JNIEnv* env, jobject obj,
+ WebCore::HistoryItem* item) {
+ JavaVM* vm;
+ mJVM = env->GetJavaVM(&vm) >= 0 ? vm : NULL;
+ mObject = adoptGlobalRef(env, obj);
+ mScale = 100;
+ mActive = false;
+ mParent = NULL;
+ mHistoryItem = item;
+}
+
+WebHistoryItem::~WebHistoryItem() {
+ if (mObject) {
+ JNIEnv* env;
+ env = mJVM->GetEnv((void **)&env, JNI_VERSION_1_4) >= 0 ? env : NULL;
+ if (!env)
+ return;
+ env->DeleteGlobalRef(mObject);
+ }
+}
+
+void WebHistoryItem::updateHistoryItem(WebCore::HistoryItem* item) {
+#ifdef ANDROID_HISTORY_CLIENT
+ // Do not want to update during inflation.
+ if (!mActive)
+ return;
+ WebHistoryItem* webItem = this;
+ // Now we need to update the top-most WebHistoryItem based on the top-most
+ // HistoryItem.
+ if (mParent) {
+ webItem = mParent.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();
+ }
+ JNIEnv* env;
+ env = webItem->mJVM->GetEnv((void **)&env, JNI_VERSION_1_4) >= 0 ? env : NULL;
+ if (!env)
+ return;
+
+ // Don't do anything if the item has been gc'd already
+ AutoJObject realItem = getRealObject(env, webItem->mObject);
+ if (!realItem.get())
+ return;
+
+ const WebCore::String& urlString = item->urlString();
+ jstring urlStr = NULL;
+ if (!urlString.isNull())
+ urlStr = env->NewString((unsigned short*)urlString.characters(), urlString.length());
+ const WebCore::String& originalUrlString = item->originalURLString();
+ jstring originalUrlStr = NULL;
+ if (!originalUrlString.isNull()) {
+ originalUrlStr = env->NewString(
+ (unsigned short*) originalUrlString.characters(),
+ originalUrlString.length());
+ }
+ const WebCore::String& titleString = item->title();
+ jstring titleStr = NULL;
+ if (!titleString.isNull())
+ titleStr = env->NewString((unsigned short*)titleString.characters(), titleString.length());
+
+ // 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;
+ WebCore::String url = item->urlString();
+ if (item->url().hasRef()) {
+ 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);
+#endif
+}
+
+static void historyItemChanged(WebCore::HistoryItem* item) {
+#ifdef ANDROID_HISTORY_CLIENT
+ LOG_ASSERT(item,
+ "historyItemChanged called with a null item");
+ if (item->bridge())
+ item->bridge()->updateHistoryItem(item);
+#endif
+}
+
+void WebHistory::AddItem(const AutoJObject& list, WebCore::HistoryItem* item)
+{
+#ifdef ANDROID_HISTORY_CLIENT
+ 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);
+
+ // 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);
+#endif
+}
+
+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()->SetIntField(list.get(), gWebBackForwardList.mCurrentIndex, newIndex);
+}
+
+static void write_string(WTF::Vector<char>& v, const WebCore::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 referrer
+ write_string(v, item->formReferrer());
+
+ // Form data
+ const WebCore::FormData* formData = item->formData();
+ if (formData)
+ write_string(v, formData->flattenToString());
+ else
+ write_string(v, WebCore::String()); // Empty constructor does not allocate a buffer.
+
+ // Target
+ write_string(v, item->target());
+
+#ifdef ANDROID_HISTORY_CLIENT
+ WebHistoryItem* bridge = item->bridge();
+ LOG_ASSERT(bridge, "We should have a bridge here!");
+ // Screen scale
+ int scale = bridge->scale();
+ LOGV("Writing scale %d", scale);
+ v.append((char*)&scale, sizeof(int));
+#endif
+
+ // Document state
+ const WTF::Vector<WebCore::String>& docState = item->documentState();
+ WTF::Vector<WebCore::String>::const_iterator end = docState.end();
+ unsigned stateSize = docState.size();
+ LOGV("Writing docState %d", stateSize);
+ v.append((char*)&stateSize, sizeof(unsigned));
+ for (WTF::Vector<WebCore::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();
+#ifdef ANDROID_HISTORY_CLIENT
+ LOG_ASSERT(parent->bridge(),
+ "The parent item should have a bridge object!");
+ if (!item->bridge()) {
+ WebHistoryItem* bridge = new 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.
+ LOG_ASSERT(parent->bridge()->parent() == NULL ||
+ item->bridge()->parent() == parent->bridge(),
+ "Somehow this item has an incorrect parent");
+ item->bridge()->setParent(parent->bridge());
+ }
+#endif
+ 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.
+ WebCore::String formContentType;
+ WebCore::String formReferrer;
+ 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 referrer
+ memcpy(&l, data, sizeofUnsigned);
+ data += sizeofUnsigned;
+ if (l) {
+ LOGV("Referrer %d %.*s", l, l, data);
+ if (data + l < end)
+ formReferrer = 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;
+ }
+ if (end - data < sizeofUnsigned)
+ return false;
+
+ // Set up the form info
+ if (formData != NULL) {
+ WebCore::ResourceRequest r;
+ r.setHTTPMethod("POST");
+ r.setHTTPContentType(formContentType);
+ r.setHTTPReferrer(formReferrer);
+ 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;
+
+#ifdef ANDROID_HISTORY_CLIENT
+ WebHistoryItem* bridge = newItem->bridge();
+ LOG_ASSERT(bridge, "There should be a bridge object during inflate");
+ // Read the screen scale
+ memcpy(&l, data, sizeofUnsigned);
+ LOGV("Screen scale %d", l);
+ bridge->setScale(l);
+ data += sizeofUnsigned;
+ if (end - data < sizeofUnsigned)
+ return false;
+#endif
+
+ // 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<WebCore::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();
+#ifdef ANDROID_HISTORY_CLIENT
+ // Set a bridge that will not call into java.
+ child->setBridge(new WebHistoryItem(bridge));
+#endif
+ // Read the child item.
+ if (!read_item_recursive(child.get(), pData, end - data)) {
+ child.clear();
+ return false;
+ }
+#ifdef ANDROID_HISTORY_CLIENT
+ child->bridge()->setActive();
+#endif
+ 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();
+#ifdef ANDROID_HISTORY_CLIENT
+ testItem->setBridge(new WebHistoryItem(NULL));
+#endif
+ 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 referrer
+ 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 referrer 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!");
+#ifdef ANDROID_HISTORY_CLIENT
+ offset += 4; // Scale
+#endif
+ // 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!");
+
+#ifdef ANDROID_HISTORY_CLIENT
+ offset = 36;
+#else
+ offset = 28;
+#endif
+ // 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 register_webhistory(JNIEnv* env)
+{
+#ifdef ANDROID_HISTORY_CLIENT
+ // Get notified of all changes to history items.
+ WebCore::notifyHistoryItemChanged = historyItemChanged;
+#endif
+#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");
+
+ // Find the WebBackForwardList object, the addHistoryItem and
+ // removeHistoryItem methods and the mCurrentIndex field.
+ 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.mCurrentIndex = env->GetFieldID(clazz, "mCurrentIndex", "I");
+ LOG_ASSERT(gWebBackForwardList.mCurrentIndex, "Could not find field mCurrentIndex");
+
+ 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/WebKit/android/jni/WebHistory.h b/WebKit/android/jni/WebHistory.h
new file mode 100644
index 0000000..7d9ff2a
--- /dev/null
+++ b/WebKit/android/jni/WebHistory.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2006, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_WEBKIT_WEBHISTORY_H
+#define ANDROID_WEBKIT_WEBHISTORY_H
+
+#include <jni.h>
+#include <wtf/RefCounted.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+ class HistoryItem;
+}
+
+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);
+};
+
+class WebHistoryItem : public WTF::RefCounted<WebHistoryItem> {
+public:
+ WebHistoryItem(WebHistoryItem* parent)
+ : mParent(parent)
+ , mObject(NULL)
+ , mJVM(NULL)
+ , mScale(100)
+ , mActive(false)
+ , mHistoryItem(NULL) {}
+ WebHistoryItem(JNIEnv*, jobject, WebCore::HistoryItem*);
+ ~WebHistoryItem();
+ void updateHistoryItem(WebCore::HistoryItem* item);
+ void setScale(int s) { mScale = s; }
+ void setActive() { mActive = true; }
+ void setParent(WebHistoryItem* parent) { mParent = parent; }
+ WebHistoryItem* parent() { return mParent.get(); }
+ int scale() { return mScale; }
+ WebCore::HistoryItem* historyItem() { return mHistoryItem; }
+private:
+ RefPtr<WebHistoryItem> mParent;
+ jobject mObject;
+ JavaVM* mJVM;
+ int mScale;
+ bool mActive;
+ WebCore::HistoryItem* mHistoryItem;
+};
+
+};
+
+#endif
diff --git a/WebKit/android/jni/WebIconDatabase.cpp b/WebKit/android/jni/WebIconDatabase.cpp
new file mode 100644
index 0000000..7bc0ae5
--- /dev/null
+++ b/WebKit/android/jni/WebIconDatabase.cpp
@@ -0,0 +1,227 @@
+/*
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define LOG_TAG "favicons"
+
+#include <config.h>
+#include <wtf/Platform.h>
+
+#include "WebIconDatabase.h"
+
+#include "IconDatabase.h"
+#include "Image.h"
+#include "IntRect.h"
+#include "JavaSharedClient.h"
+#include "jni_utility.h"
+#include "KURL.h"
+#include "WebCoreJni.h"
+
+#include <pthread.h>
+#include "GraphicsJNI.h"
+#include <SkBitmap.h>
+#include <SkImageDecoder.h>
+#include <SkTemplates.h>
+#include <utils/misc.h>
+#include <JNIHelp.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 WebCore::String& pageURL)
+{
+ // If there are no clients currently, drop this message.
+ if (mClients.size() == 0)
+ return;
+
+ // Attempt to attach to the current vm.
+ JavaVM* vm = JSC::Bindings::getJavaVM();
+ JavaVMAttachArgs args;
+
+ args.version = JNI_VERSION_1_4;
+ args.name = "IconDatabase";
+ args.group = NULL;
+
+ JNIEnv* env;
+ bool threadIsAttached = true;
+ if (vm->AttachCurrentThread(&env, (void*) &args) != JNI_OK) {
+ LOGE("Could not attach IconDatabase thread to the VM");
+ threadIsAttached = false;
+ }
+
+ mNotificationsMutex.lock();
+ mNotifications.append(pageURL);
+ if (!mDeliveryRequested) {
+ if (threadIsAttached) {
+ mDeliveryRequested = true;
+ WebCore::JavaSharedClient::EnqueueFunctionPtr(DeliverNotifications, this);
+ }
+ }
+ mNotificationsMutex.unlock();
+}
+
+// Called in the WebCore thread
+void WebIconDatabase::RegisterForIconNotification(WebIconDatabaseClient* client)
+{
+ gIconDatabaseClient->mClientsMutex.lock();
+ gIconDatabaseClient->mClients.append(client);
+ gIconDatabaseClient->mClientsMutex.unlock();
+}
+
+// Called in the WebCore thread
+void WebIconDatabase::UnregisterForIconNotification(WebIconDatabaseClient* client)
+{
+ WebIconDatabase* db = gIconDatabaseClient;
+ db->mClientsMutex.lock();
+ for (unsigned i = 0; i < db->mClients.size(); ++i) {
+ if (db->mClients[i] == client) {
+ db->mClients.remove(i);
+ break;
+ }
+ }
+ db->mClientsMutex.unlock();
+}
+
+// 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<WebCore::String> queue;
+ mNotificationsMutex.lock();
+ queue.swap(mNotifications);
+ mDeliveryRequested = false;
+ mNotificationsMutex.unlock();
+
+ // Swap the clients queue
+ Vector<WebIconDatabaseClient*> clients;
+ mClientsMutex.lock();
+ clients.swap(mClients);
+ mClientsMutex.unlock();
+
+ 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");
+ WebCore::String pathStr = to_string(env, path);
+ LOGV("Opening WebIconDatabase file '%s'", pathStr.latin1().data());
+ bool res = iconDb->open(pathStr);
+ if (!res)
+ LOGE("Open failed!");
+}
+
+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");
+ WebCore::String urlStr = to_string(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");
+ WebCore::String urlStr = to_string(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");
+ WebCore::String urlStr = to_string(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 register_webicondatabase(JNIEnv* env)
+{
+ jclass webIconDB = env->FindClass("android/webkit/WebIconDatabase");
+ LOG_ASSERT(webIconDB, "Unable to find class android.webkit.WebIconDatabase");
+
+ return jniRegisterNativeMethods(env, "android/webkit/WebIconDatabase",
+ gWebIconDatabaseMethods, NELEM(gWebIconDatabaseMethods));
+}
+
+}
diff --git a/WebKit/android/jni/WebIconDatabase.h b/WebKit/android/jni/WebIconDatabase.h
new file mode 100644
index 0000000..eefe1f6
--- /dev/null
+++ b/WebKit/android/jni/WebIconDatabase.h
@@ -0,0 +1,69 @@
+/* //device/libs/WebKitLib/WebKit/WebCore/platform/android/jni/android_webkit_webicondatabase.h
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_WEBKIT_WEBICONDATABASE_H
+#define ANDROID_WEBKIT_WEBICONDATABASE_H
+
+#include "IconDatabaseClient.h"
+#include "utils/threads.h"
+#include "wtf/Vector.h"
+
+#include <jni.h>
+
+namespace WebCore {
+ class Image;
+ class String;
+}
+
+namespace android {
+
+ class WebIconDatabaseClient {
+ public:
+ virtual ~WebIconDatabaseClient() {}
+ virtual void didAddIconForPageUrl(const WebCore::String& pageUrl) = 0;
+ };
+
+ class WebIconDatabase : public WebCore::IconDatabaseClient {
+ public:
+ WebIconDatabase() : mDeliveryRequested(false) {}
+ // IconDatabaseClient method
+ virtual void dispatchDidAddIconForPageURL(const WebCore::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 and a mutex to protect it.
+ Vector<WebIconDatabaseClient*> mClients;
+ android::Mutex mClientsMutex;
+
+ // Queue of page urls that have received an icon.
+ Vector<WebCore::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/WebKit/android/jni/WebSettings.cpp b/WebKit/android/jni/WebSettings.cpp
new file mode 100644
index 0000000..eb34fb8
--- /dev/null
+++ b/WebKit/android/jni/WebSettings.cpp
@@ -0,0 +1,325 @@
+/*
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define LOG_TAG "websettings"
+
+#include <config.h>
+#include <wtf/Platform.h>
+
+#include "Document.h"
+#include "Frame.h"
+#include "FrameLoader.h"
+#include "FrameView.h"
+#include "DocLoader.h"
+#include "Page.h"
+#include "RenderTable.h"
+#ifdef ANDROID_PLUGINS
+#include "PlatformString.h"
+#include "PluginDatabase.h"
+#endif
+#include "Settings.h"
+#include "WebCoreFrameBridge.h"
+#include "WebCoreJni.h"
+
+#include <JNIHelp.h>
+#include <utils/misc.h>
+
+namespace WebCore {
+// Defined in FileSystemAndroid.cpp
+extern String sPluginPath;
+}
+
+namespace android {
+
+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;");
+ 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
+ mJavaScriptEnabled = env->GetFieldID(clazz, "mJavaScriptEnabled", "Z");
+ mPluginsEnabled = env->GetFieldID(clazz, "mPluginsEnabled", "Z");
+#ifdef ANDROID_PLUGINS
+ mPluginsPath = env->GetFieldID(clazz, "mPluginsPath", "Ljava/lang/String;");
+#endif
+ mJavaScriptCanOpenWindowsAutomatically = env->GetFieldID(clazz,
+ "mJavaScriptCanOpenWindowsAutomatically", "Z");
+ mUseWideViewport = env->GetFieldID(clazz, "mUseWideViewport", "Z");
+ mSupportMultipleWindows = env->GetFieldID(clazz, "mSupportMultipleWindows", "Z");
+ mShrinksStandaloneImagesToFit = env->GetFieldID(clazz, "mShrinksStandaloneImagesToFit", "Z");
+ mUseDoubleTree = env->GetFieldID(clazz, "mUseDoubleTree", "Z");
+
+ 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(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(mJavaScriptEnabled, "Could not find field mJavaScriptEnabled");
+ LOG_ASSERT(mPluginsEnabled, "Could not find field mPluginsEnabled");
+#ifdef ANDROID_PLUGINS
+ LOG_ASSERT(mPluginsPath, "Could not find field mPluginsPath");
+#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(mUseDoubleTree, "Could not find field mUseDoubleTree");
+
+ jclass c = env->FindClass("java/lang/Enum");
+ LOG_ASSERT(c, "Could not find Enum class!");
+ mOrdinal = env->GetMethodID(c, "ordinal", "()I");
+ LOG_ASSERT(mOrdinal, "Could not find method ordinal");
+ c = env->FindClass("android/webkit/WebSettings$TextSize");
+ LOG_ASSERT(c, "Could not find TextSize enum");
+ mTextSizeValue = env->GetFieldID(c, "value", "I");
+ }
+
+ // Field ids
+ jfieldID mLayoutAlgorithm;
+ jfieldID mTextSize;
+ jfieldID mStandardFontFamily;
+ jfieldID mFixedFontFamily;
+ jfieldID mSansSerifFontFamily;
+ jfieldID mSerifFontFamily;
+ jfieldID mCursiveFontFamily;
+ jfieldID mFantasyFontFamily;
+ jfieldID mDefaultTextEncoding;
+ jfieldID mUserAgent;
+ jfieldID mMinimumFontSize;
+ jfieldID mMinimumLogicalFontSize;
+ jfieldID mDefaultFontSize;
+ jfieldID mDefaultFixedFontSize;
+ jfieldID mLoadsImagesAutomatically;
+#ifdef ANDROID_BLOCK_NETWORK_IMAGE
+ jfieldID mBlockNetworkImage;
+#endif
+ jfieldID mJavaScriptEnabled;
+ jfieldID mPluginsEnabled;
+#ifdef ANDROID_PLUGINS
+ jfieldID mPluginsPath;
+#endif
+ jfieldID mJavaScriptCanOpenWindowsAutomatically;
+ jfieldID mUseWideViewport;
+ jfieldID mSupportMultipleWindows;
+ jfieldID mShrinksStandaloneImagesToFit;
+ jfieldID mUseDoubleTree;
+
+ // Ordinal() method and value field for enums
+ jmethodID mOrdinal;
+ jfieldID mTextSizeValue;
+};
+
+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);
+}
+
+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::DocLoader* docLoader = pFrame->document()->docLoader();
+
+#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()->updateStyleSelector();
+ 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->zoomFactor() != zoomFactor)
+ pFrame->setZoomFactor(zoomFactor, /*isTextOnly*/true);
+
+ jstring str = (jstring)env->GetObjectField(obj, gFieldIds->mStandardFontFamily);
+ s->setStandardFontFamily(to_string(env, str));
+
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mFixedFontFamily);
+ s->setFixedFontFamily(to_string(env, str));
+
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mSansSerifFontFamily);
+ s->setSansSerifFontFamily(to_string(env, str));
+
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mSerifFontFamily);
+ s->setSerifFontFamily(to_string(env, str));
+
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mCursiveFontFamily);
+ s->setCursiveFontFamily(to_string(env, str));
+
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mFantasyFontFamily);
+ s->setFantasyFontFamily(to_string(env, str));
+
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mDefaultTextEncoding);
+ s->setDefaultTextEncodingName(to_string(env, str));
+
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mUserAgent);
+ WebFrame::getWebFrame(pFrame)->setUserAgent(to_string(env, str));
+
+ 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)
+ docLoader->setAutoLoadImages(true);
+
+#ifdef ANDROID_BLOCK_NETWORK_IMAGE
+ flag = env->GetBooleanField(obj, gFieldIds->mBlockNetworkImage);
+ s->setBlockNetworkImage(flag);
+ if(!flag)
+ docLoader->setBlockNetworkImage(false);
+#endif
+
+ flag = env->GetBooleanField(obj, gFieldIds->mJavaScriptEnabled);
+ s->setJavaScriptEnabled(flag);
+
+ flag = env->GetBooleanField(obj, gFieldIds->mPluginsEnabled);
+ s->setPluginsEnabled(flag);
+
+#ifdef ANDROID_PLUGINS
+ ::WebCore::PluginDatabase *pluginDatabase =
+ ::WebCore::PluginDatabase::installedPlugins();
+ str = (jstring)env->GetObjectField(obj, gFieldIds->mPluginsPath);
+ if (str) {
+ WebCore::String pluginsPath = to_string(env, str);
+ s->setPluginsPath(pluginsPath);
+ // Set the plugin directories to this single entry.
+ Vector< ::WebCore::String > paths(1);
+ paths[0] = pluginsPath;
+ pluginDatabase->setPluginDirectories(paths);
+ // Set the home directory for plugin temporary files
+ WebCore::sPluginPath = paths[0];
+ // Reload plugins.
+ pluginDatabase->refresh();
+ }
+#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);
+#if USE(LOW_BANDWIDTH_DISPLAY)
+ flag = env->GetBooleanField(obj, gFieldIds->mUseDoubleTree);
+ pFrame->loader()->setUseLowBandwidthDisplay(flag);
+#endif
+ }
+};
+
+//-------------------------------------------------------------
+// JNI registration
+//-------------------------------------------------------------
+
+static JNINativeMethod gWebSettingsMethods[] = {
+ { "nativeSync", "(I)V",
+ (void*) WebSettings::Sync }
+};
+
+int register_websettings(JNIEnv* env)
+{
+ jclass clazz = env->FindClass("android/webkit/WebSettings");
+ LOG_ASSERT(clazz, "Unable to find class WebSettings!");
+ gFieldIds = new FieldIds(env, clazz);
+ return jniRegisterNativeMethods(env, "android/webkit/WebSettings",
+ gWebSettingsMethods, NELEM(gWebSettingsMethods));
+}
+
+}
diff --git a/WebKit/android/jni/WebViewCore.cpp b/WebKit/android/jni/WebViewCore.cpp
new file mode 100644
index 0000000..363f2be
--- /dev/null
+++ b/WebKit/android/jni/WebViewCore.cpp
@@ -0,0 +1,2502 @@
+/*
+ * Copyright 2006, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "webcoreglue"
+
+#include <config.h>
+#include "WebViewCore.h"
+
+#include "AtomicString.h"
+#include "CachedNode.h"
+#include "CachedRoot.h"
+#include "Color.h"
+#include "Document.h"
+#include "Element.h"
+#include "Editor.h"
+#include "EditorClientAndroid.h"
+#include "EventHandler.h"
+#include "EventNames.h"
+#include "Font.h"
+#include "FrameLoader.h"
+#include "FrameTree.h"
+#include "FrameView.h"
+#include "GraphicsContext.h"
+#include "GraphicsJNI.h"
+#include "HitTestResult.h"
+#include "HTMLAnchorElement.h"
+#include "HTMLAreaElement.h"
+#include "HTMLElement.h"
+#include "HTMLImageElement.h"
+#include "HTMLInputElement.h"
+#include "HTMLMapElement.h"
+#include "HTMLNames.h"
+#include "HTMLOptGroupElement.h"
+#include "HTMLOptionElement.h"
+#include "HTMLSelectElement.h"
+#include "HTMLTextAreaElement.h"
+#include "InlineTextBox.h"
+#include <JNIHelp.h>
+#include "KeyboardCodes.h"
+#include "Node.h"
+#include "Page.h"
+#include "PlatformKeyboardEvent.h"
+#include "PlatformString.h"
+#include "PluginInfoStore.h"
+#include "PluginWidgetAndroid.h"
+#include "Position.h"
+#include "ProgressTracker.h"
+#include "RenderLayer.h"
+#include "RenderText.h"
+#include "RenderTextControl.h"
+#include "RenderThemeAndroid.h"
+#include "RenderView.h"
+#include "ResourceRequest.h"
+#include "SelectionController.h"
+#include "Settings.h"
+#include "SkTemplates.h"
+#include "SkTypes.h"
+#include "SkCanvas.h"
+#include "SkPicture.h"
+#include "SkUtils.h"
+#include "StringImpl.h"
+#include "SystemTime.h"
+#include "Text.h"
+#include "TypingCommand.h"
+#include "WebCoreFrameBridge.h"
+#include "WebFrameView.h"
+#include "HistoryItem.h"
+#include "android_graphics.h"
+#include <ui/KeycodeLabels.h>
+
+#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 "CString.h"
+
+FILE* gDomTreeFile = 0;
+FILE* gRenderTreeFile = 0;
+#endif
+
+#ifdef ANDROID_INSTRUMENT
+static uint32_t sTotalTimeUsed = 0;
+static uint32_t sTotalPaintTimeUsed = 0;
+static uint32_t sCounter = 0;
+
+namespace WebCore {
+void Frame::resetWebViewCoreTimeCounter()
+{
+ sTotalTimeUsed = 0;
+}
+
+void Frame::reportWebViewCoreTimeCounter()
+{
+ LOG(LOG_DEBUG, "WebCore", "*-* Total native 4 (webview core) time: %d ms\n",
+ sTotalTimeUsed);
+}
+// This should be in Frame.cpp, but android LOG is conflict with webcore LOG
+void Frame::resetPaintTimeCounter()
+{
+ sTotalPaintTimeUsed = 0;
+ sCounter = 0;
+}
+
+void Frame::reportPaintTimeCounter()
+{
+ LOG(LOG_DEBUG, "WebCore", "*-* Total draw time: %d ms called %d times\n",
+ sTotalPaintTimeUsed, sCounter);
+}
+}
+#endif
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+namespace android {
+
+#ifdef ANDROID_INSTRUMENT
+class TimeCounterWV {
+public:
+ TimeCounterWV() {
+ m_startTime = WebCore::get_thread_msec();
+ }
+
+ ~TimeCounterWV() {
+ sTotalTimeUsed += WebCore::get_thread_msec() - m_startTime;
+ }
+
+private:
+ uint32_t m_startTime;
+};
+#endif
+
+// ----------------------------------------------------------------------------
+
+#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_webView;
+} gWebViewCoreFields;
+
+// ----------------------------------------------------------------------------
+
+struct WebViewCore::JavaGlue {
+ JavaVM* m_JVM;
+ jobject m_obj;
+ jmethodID m_spawnScrollTo;
+ jmethodID m_scrollTo;
+ jmethodID m_scrollBy;
+ jmethodID m_contentDraw;
+ jmethodID m_requestListBox;
+ jmethodID m_requestSingleListBox;
+ jmethodID m_jsAlert;
+ jmethodID m_jsConfirm;
+ jmethodID m_jsPrompt;
+ jmethodID m_jsUnload;
+ jmethodID m_didFirstLayout;
+ jmethodID m_sendMarkNodeInvalid;
+ jmethodID m_sendNotifyFocusSet;
+ jmethodID m_sendNotifyProgressFinished;
+ jmethodID m_sendRecomputeFocus;
+ jmethodID m_sendViewInvalidate;
+ jmethodID m_updateTextfield;
+ jmethodID m_restoreScale;
+ jmethodID m_needTouchEvents;
+ 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::gFrameGenerationMutex;
+Mutex WebViewCore::gRecomputeFocusMutex;
+Mutex WebViewCore::gButtonMutex;
+Mutex WebViewCore::m_contentMutex;
+
+WebViewCore::WebViewCore(JNIEnv* env, jobject javaWebViewCore, WebCore::Frame* mainframe)
+ : m_pluginInvalTimer(this, &WebViewCore::pluginInvalTimerFired)
+{
+ m_mainFrame = mainframe;
+
+ m_popupReply = 0;
+ m_buildGeneration = 0;
+ m_moveGeneration = 0;
+ m_generation = 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_scale = 1;
+
+ 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_JVM = jnienv_to_javavm(env);
+ m_javaGlue->m_obj = adoptGlobalRef(env, javaWebViewCore);
+ m_javaGlue->m_spawnScrollTo = GetJMethod(env, clazz, "contentSpawnScrollTo", "(II)V");
+ m_javaGlue->m_scrollTo = GetJMethod(env, clazz, "contentScrollTo", "(II)V");
+ m_javaGlue->m_scrollBy = GetJMethod(env, clazz, "contentScrollBy", "(II)V");
+ m_javaGlue->m_contentDraw = GetJMethod(env, clazz, "contentDraw", "()V");
+ m_javaGlue->m_requestListBox = GetJMethod(env, clazz, "requestListBox", "([Ljava/lang/String;[Z[I)V");
+ m_javaGlue->m_requestSingleListBox = GetJMethod(env, clazz, "requestListBox", "([Ljava/lang/String;[ZI)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_didFirstLayout = GetJMethod(env, clazz, "didFirstLayout", "()V");
+ m_javaGlue->m_sendMarkNodeInvalid = GetJMethod(env, clazz, "sendMarkNodeInvalid", "(I)V");
+ m_javaGlue->m_sendNotifyFocusSet = GetJMethod(env, clazz, "sendNotifyFocusSet", "()V");
+ m_javaGlue->m_sendNotifyProgressFinished = GetJMethod(env, clazz, "sendNotifyProgressFinished", "()V");
+ m_javaGlue->m_sendRecomputeFocus = GetJMethod(env, clazz, "sendRecomputeFocus", "()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_restoreScale = GetJMethod(env, clazz, "restoreScale", "(I)V");
+ m_javaGlue->m_needTouchEvents = GetJMethod(env, clazz, "needTouchEvents", "(Z)V");
+
+ env->SetIntField(javaWebViewCore, gWebViewCoreFields.m_nativeClass, (jint)this);
+
+ m_scrollOffsetX = m_scrollOffsetY = 0;
+
+ reset(true);
+}
+
+WebViewCore::~WebViewCore()
+{
+ // Release the focused view
+ Release(m_popupReply);
+
+ if (m_javaGlue->m_obj) {
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ env->DeleteGlobalRef(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)
+{
+ WebFrameView* webFrameView = static_cast<WebFrameView*>(view->platformWidget());
+ 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_lastFocusedBounds = WebCore::IntRect(0,0,0,0);
+ clearContent();
+ m_updatedFrameCache = true;
+ m_frameCacheOutOfDate = true;
+ m_blockFocusChange = false;
+ m_snapAnchorNode = 0;
+ m_useReplay = false;
+ m_skipContentDraw = false;
+ m_findIsUp = false;
+}
+
+static bool layoutIfNeededRecursive(WebCore::Frame* f)
+{
+ if (!f)
+ return true;
+
+ WebCore::FrameView* v = f->view();
+ if (!v)
+ return true;
+
+ if (v->needsLayout())
+ v->layout();
+
+ WebCore::Frame* child = f->tree()->firstChild();
+ bool success = true;
+ while (child) {
+ success &= layoutIfNeededRecursive(child);
+ child = child->tree()->nextSibling();
+ }
+
+ return success && !v->needsLayout();
+}
+
+void WebViewCore::recordPicture(SkPicture* picture)
+{
+ // if there is no document yet, just return
+ if (!m_mainFrame->document())
+ return;
+ // Call layout to ensure that the contentWidth and contentHeight are correct
+ if (!layoutIfNeededRecursive(m_mainFrame))
+ return;
+ // draw into the picture's recording canvas
+ WebCore::FrameView* view = m_mainFrame->view();
+ SkAutoPictureRecord arp(picture, view->contentsWidth(), view->contentsHeight());
+ 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, INT_MAX, INT_MAX));
+
+ 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 (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;
+ // 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();
+ 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);
+ picture->safeUnref();
+ }
+ // 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);
+ WebCore::CacheBuilder& builder = m_mainFrame->getCacheBuilder();
+ WebCore::Node* oldFocusNode = builder.currentFocus();
+ m_frameCacheOutOfDate = true;
+ WebCore::IntRect oldBounds = oldFocusNode ?
+ oldFocusNode->getRect() : WebCore::IntRect(0,0,0,0);
+ DBG_NAV_LOGD_THROTTLE("m_lastFocused=%p oldFocusNode=%p"
+ " m_lastFocusedBounds={%d,%d,%d,%d} oldBounds={%d,%d,%d,%d}",
+ m_lastFocused, oldFocusNode,
+ m_lastFocusedBounds.x(), m_lastFocusedBounds.y(), m_lastFocusedBounds.width(), m_lastFocusedBounds.height(),
+ oldBounds.x(), oldBounds.y(), oldBounds.width(), oldBounds.height());
+ if (m_lastFocused != oldFocusNode || m_lastFocusedBounds != oldBounds
+ || m_findIsUp) {
+ m_lastFocused = oldFocusNode;
+ m_lastFocusedBounds = oldBounds;
+ DBG_NAV_LOG("call updateFrameCache");
+ updateFrameCache();
+ }
+}
+
+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++;
+ }
+ }
+}
+
+void WebViewCore::clearContent()
+{
+ DBG_SET_LOG("");
+ m_contentMutex.lock();
+ m_content.clear();
+ m_contentMutex.unlock();
+ m_addInval.setEmpty();
+}
+
+void WebViewCore::copyContentToPicture(SkPicture* picture)
+{
+ DBG_SET_LOG("start");
+ m_contentMutex.lock();
+ PictureSet copyContent = PictureSet(m_content);
+ m_contentMutex.unlock();
+ copyContent.toPicture(picture);
+ DBG_SET_LOG("end");
+}
+
+bool WebViewCore::drawContent(SkCanvas* canvas, SkColor color)
+{
+ DBG_SET_LOG("start");
+ m_contentMutex.lock();
+ PictureSet copyContent = PictureSet(m_content);
+ m_contentMutex.unlock();
+ int sc = canvas->save(SkCanvas::kClip_SaveFlag);
+ SkRect clip;
+ clip.set(0, 0, copyContent.width(), copyContent.height());
+ canvas->clipRect(clip, SkRegion::kDifference_Op);
+ canvas->drawColor(color);
+ canvas->restoreToCount(sc);
+ bool tookTooLong = copyContent.draw(canvas);
+ m_contentMutex.lock();
+ m_content.setDrawTimes(copyContent);
+ m_contentMutex.unlock();
+ DBG_SET_LOG("end");
+ return tookTooLong;
+}
+
+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);
+ 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()));
+
+ 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("draw [%d] {%d,%d,w=%d,h=%d}", index, inval.fLeft,
+ inval.fTop, inval.width(), inval.height());
+ pictureSet->setPicture(index, rebuildPicture(inval));
+ }
+ pictureSet->validate(__FUNCTION__);
+}
+
+bool WebViewCore::recordContent(SkRegion* region, SkIPoint* point)
+{
+ DBG_SET_LOG("start");
+ m_contentMutex.lock();
+ PictureSet contentCopy(m_content);
+ m_contentMutex.unlock();
+ recordPictureSet(&contentCopy);
+ float progress = (float) m_mainFrame->page()->progress()->estimatedProgress();
+ if (progress > 0.0f && progress < 1.0f && contentCopy.isEmpty()) {
+ DBG_SET_LOGD("empty (progress=%g)", progress);
+ return false;
+ }
+ region->set(m_addInval);
+ m_addInval.setEmpty();
+ m_contentMutex.lock();
+ contentCopy.setDrawTimes(m_content);
+ m_content.set(contentCopy);
+ point->fX = m_content.width();
+ point->fY = m_content.height();
+ m_contentMutex.unlock();
+ 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 true;
+}
+
+void WebViewCore::splitContent()
+{
+ bool layoutSuceeded = layoutIfNeededRecursive(m_mainFrame);
+ LOG_ASSERT(layoutSuceeded, "Can never be called recursively");
+ PictureSet tempPictureSet;
+ m_contentMutex.lock();
+ m_content.split(&tempPictureSet);
+ m_contentMutex.unlock();
+ rebuildPictureSet(&tempPictureSet);
+ m_contentMutex.lock();
+ m_content.set(tempPictureSet);
+ m_contentMutex.unlock();
+}
+
+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 = javavm_to_jnienv(m_javaGlue->m_JVM);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), animate ? m_javaGlue->m_spawnScrollTo : m_javaGlue->m_scrollTo, x, y);
+ checkException(env);
+}
+
+void WebViewCore::sendMarkNodeInvalid(WebCore::Node* node)
+{
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_sendMarkNodeInvalid, (int) node);
+ checkException(env);
+}
+
+void WebViewCore::sendNotifyFocusSet()
+{
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_sendNotifyFocusSet);
+ checkException(env);
+}
+
+void WebViewCore::sendNotifyProgressFinished()
+{
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_sendNotifyProgressFinished);
+ checkException(env);
+}
+
+void WebViewCore::sendRecomputeFocus()
+{
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_sendRecomputeFocus);
+ checkException(env);
+}
+
+void WebViewCore::viewInvalidate(const SkIRect& rect)
+{
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_sendViewInvalidate,
+ rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
+ 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 = javavm_to_jnienv(m_javaGlue->m_JVM);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_sendViewInvalidate,
+ rect.x(), rect.y(), rect.right(), rect.bottom());
+ checkException(env);
+}
+
+void WebViewCore::scrollBy(int dx, int dy)
+{
+ if (!(dx | dy))
+ return;
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_scrollBy, dx, dy);
+ checkException(env);
+}
+
+void WebViewCore::contentDraw()
+{
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_contentDraw);
+ 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, max;
+ android_setrect(&rect, r);
+ max.set(0, 0, INT_MAX, INT_MAX);
+ if (!rect.intersect(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::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!");
+
+ const WebCore::KURL& url = m_mainFrame->loader()->url();
+ if (url.isEmpty())
+ return;
+ LOGV("::WebCore:: didFirstLayout %s", url.string().ascii().data());
+
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_didFirstLayout);
+ checkException(env);
+
+ DBG_NAV_LOG("call updateFrameCache");
+ updateFrameCache();
+ m_history.setDidFirstLayout(true);
+}
+
+void WebViewCore::restoreScale(int scale)
+{
+ DEBUG_NAV_UI_LOGD("%s", __FUNCTION__);
+ LOG_ASSERT(m_javaGlue->m_obj, "A Java widget was not associated with this view bridge!");
+
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_restoreScale, scale);
+ 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) // Android
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_needTouchEvents, need);
+ checkException(env);
+#endif
+}
+
+void WebViewCore::notifyFocusSet()
+{
+ sendNotifyFocusSet();
+}
+
+void WebViewCore::notifyProgressFinished()
+{
+ DBG_NAV_LOG("call updateFrameCache");
+ updateFrameCache();
+ sendNotifyProgressFinished();
+}
+
+void WebViewCore::doMaxScroll(WebCore::CacheBuilder::Direction dir)
+{
+ int dx = 0, dy = 0;
+
+ switch (dir) {
+ case WebCore::CacheBuilder::LEFT:
+ dx = -m_maxXScroll;
+ break;
+ case WebCore::CacheBuilder::UP:
+ dy = -m_maxYScroll;
+ break;
+ case WebCore::CacheBuilder::RIGHT:
+ dx = m_maxXScroll;
+ break;
+ case WebCore::CacheBuilder::DOWN:
+ dy = m_maxYScroll;
+ break;
+ case WebCore::CacheBuilder::UNINITIALIZED:
+ default:
+ LOG_ASSERT(0, "unexpected focus selector");
+ }
+ this->scrollBy(dx, dy);
+}
+
+void WebViewCore::setScrollOffset(int dx, int dy)
+{
+ DBG_NAV_LOGD("{%d,%d}", dx, dy);
+ if (m_scrollOffsetX != dx || m_scrollOffsetY != dy) {
+ m_scrollOffsetX = dx;
+ m_scrollOffsetY = dy;
+ m_mainFrame->sendScrollEvent();
+ // 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);
+ }
+}
+
+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 screenWidth, int scale)
+{
+ WebCoreViewBridge* window = m_mainFrame->view()->platformWidget();
+ int ow = window->width();
+ int oh = window->height();
+ window->setSize(width, height);
+ int osw = m_screenWidth;
+ m_screenWidth = screenWidth;
+ m_scale = scale;
+ m_maxXScroll = screenWidth >> 2;
+ m_maxYScroll = (screenWidth * height / width) >> 2;
+ DBG_NAV_LOGD("old:(w=%d,h=%d,s=%d) new:(w=%d,h=%d,s=%d)",
+ ow, oh, osw, width, height, screenWidth);
+ if (ow != width || oh != height || osw != screenWidth) {
+ WebCore::RenderObject *r = m_mainFrame->contentRenderer();
+ DBG_NAV_LOGD("renderer=%p", r);
+ if (r) {
+ r->setNeedsLayoutAndPrefWidthsRecalc();
+ m_mainFrame->forceLayout(true);
+ }
+ }
+}
+
+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
+ if (useFile)
+ gRenderTreeFile = fopen(RENDER_TREE_LOG_FILE, "w");
+ WebCore::CString renderDump = WebCore::externalRepresentation(m_mainFrame->contentRenderer()).utf8();
+ const char* data = renderDump.data();
+ int length = renderDump.length();
+ int last = 0;
+ for (int i = 0; i < length; i++) {
+ if (data[i] == '\n') {
+ if (i != last) {
+ char* chunk = new char[i - last + 1];
+ strncpy(chunk, (data + last), i - last);
+ chunk[i - last] = '\0';
+ DUMP_RENDER_LOGD("%s", chunk);
+ }
+ last = i + 1;
+ }
+ }
+ if (gRenderTreeFile) {
+ fclose(gRenderTreeFile);
+ gRenderTreeFile = 0;
+ }
+#endif
+}
+
+void WebViewCore::dumpNavTree()
+{
+#if DUMP_NAV_CACHE
+ m_mainFrame->getCacheBuilder().mDebug.print();
+#endif
+}
+
+WebCore::String WebViewCore::retrieveHref(WebCore::Frame* frame, WebCore::Node* node)
+{
+ WebCore::CacheBuilder& builder = m_mainFrame->getCacheBuilder();
+ if (!builder.validNode(frame, node))
+ return WebCore::String();
+ if (!node->hasTagName(WebCore::HTMLNames::aTag))
+ return WebCore::String();
+ WebCore::HTMLAnchorElement* anchor = static_cast<WebCore::HTMLAnchorElement*>(node);
+ return anchor->href();
+}
+
+bool WebViewCore::prepareFrameCache()
+{
+ if (!m_frameCacheOutOfDate) {
+ DBG_NAV_LOG("!m_frameCacheOutOfDate");
+ return false;
+ }
+ m_frameCacheOutOfDate = false;
+#if DEBUG_NAV_UI
+ DBG_NAV_LOG("m_frameCacheOutOfDate was true");
+ m_now = SkTime::GetMSecs();
+#endif
+ m_temp = new CachedRoot();
+ m_temp->init(m_mainFrame, &m_history);
+ m_temp->setGeneration(++m_buildGeneration);
+ WebCore::CacheBuilder& builder = m_mainFrame->getCacheBuilder();
+ 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);
+ return true;
+}
+
+void WebViewCore::releaseFrameCache(bool newCache)
+{
+ if (!newCache) {
+ DBG_NAV_LOG("!newCache");
+ return;
+ }
+ 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();
+ notifyFocusSet();
+ // it's tempting to send an invalidate here, but it's a bad idea
+ // the cache is now up to date, but the focus is not -- the event
+ // may need to be recomputed from the prior history. An invalidate
+ // will draw the stale location causing the ring to flash at the wrong place.
+}
+
+void WebViewCore::updateFrameCache()
+{
+ m_useReplay = false;
+ releaseFrameCache(prepareFrameCache());
+}
+
+void WebViewCore::removeFrameGeneration(WebCore::Frame* frame)
+{
+ DBG_NAV_LOGD("frame=%p m_generation=%d", frame, m_generation);
+ gFrameGenerationMutex.lock();
+ int last = m_frameGenerations.size() - 1;
+ for (int index = 0; index <= last; index++) {
+ if (m_frameGenerations[index].m_frame == frame) {
+ DBG_NAV_LOGD("index=%d last=%d", index, last);
+ if (index != last)
+ m_frameGenerations[index] = m_frameGenerations[last];
+ m_frameGenerations.removeLast();
+ break;
+ }
+ }
+ gFrameGenerationMutex.unlock();
+}
+
+void WebViewCore::updateFrameGeneration(WebCore::Frame* frame)
+{
+ DBG_NAV_LOGD("frame=%p m_generation=%d", frame, m_generation);
+ gFrameGenerationMutex.lock();
+ ++m_buildGeneration;
+ for (size_t index = 0; index < m_frameGenerations.size(); index++) {
+ if (m_frameGenerations[index].m_frame == frame) {
+ DBG_NAV_LOG("replace");
+ m_frameGenerations[index].m_generation = m_buildGeneration;
+ goto done;
+ }
+ }
+ {
+ FrameGen frameGen = {frame, m_buildGeneration};
+ m_frameGenerations.append(frameGen);
+ DBG_NAV_LOG("append");
+ }
+done:
+ gFrameGenerationMutex.unlock();
+}
+
+int WebViewCore::retrieveFrameGeneration(WebCore::Frame* frame)
+{
+ int result = INT_MAX;
+ gFrameGenerationMutex.lock();
+ for (size_t index = 0; index < m_frameGenerations.size(); index++) {
+ if (m_frameGenerations[index].m_frame == frame) {
+ result = m_frameGenerations[index].m_generation;
+ break;
+ }
+ }
+ gFrameGenerationMutex.unlock();
+ DBG_NAV_LOGD("frame=%p m_generation=%d result=%d", frame, m_generation, result);
+ return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void WebViewCore::addPlugin(PluginWidgetAndroid* w)
+{
+ SkDebugf("----------- addPlugin %p", w);
+ *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);
+ }
+}
+
+void WebViewCore::invalPlugin(PluginWidgetAndroid* w)
+{
+ const double PLUGIN_INVAL_DELAY = 0; // should this be non-zero?
+
+ 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();
+ w->localToPageCoords(&dirty);
+ inval.op(dirty, SkRegion::kUnion_Op);
+ }
+ }
+
+ if (!inval.isEmpty()) {
+ // inval.getBounds() is our rectangle
+ this->viewInvalidate(inval.getBounds());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void WebViewCore::setFinalFocus(WebCore::Frame* frame, WebCore::Node* node,
+ int x, int y, bool block)
+{
+ DBG_NAV_LOGD("frame=%p node=%p x=%d y=%d", frame, node, x, y);
+ bool result = finalKitFocus(frame, node, x, y);
+ if (block) {
+ m_blockFocusChange = true;
+ if (!result && node)
+ touchUp(m_touchGeneration, 0, 0, 0, x, y, 0, true, true);
+ }
+}
+
+void WebViewCore::setKitFocus(int moveGeneration, int buildGeneration,
+ WebCore::Frame* frame, WebCore::Node* node, int x, int y,
+ bool ignoreNullFocus)
+{
+ DBG_NAV_LOGD("m_moveGeneration=%d moveGeneration=%d"
+ " buildGeneration=%d frame=%p node=%p x=%d y=%d",
+ m_moveGeneration, moveGeneration, buildGeneration, frame, node, x, y);
+ if (m_blockFocusChange) {
+ DBG_NAV_LOG("m_blockFocusChange");
+ return;
+ }
+ 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
+ }
+ if (!commonKitFocus(moveGeneration, buildGeneration, frame, node, x, y,
+ ignoreNullFocus))
+ return;
+ m_lastGeneration = moveGeneration;
+}
+
+bool WebViewCore::commonKitFocus(int generation, int buildGeneration,
+ WebCore::Frame* frame, WebCore::Node* node, int x, int y,
+ bool ignoreNullFocus)
+{
+ DBG_NAV_LOGD("generation=%d buildGeneration=%d frame=%p"
+ " node=%p x=%d y=%d", generation, buildGeneration, frame, node, x, y);
+ m_useReplay = true;
+ bool newCache = prepareFrameCache(); // must wait for possible recompute before using
+ if (m_moveGeneration > generation) {
+ DBG_NAV_LOGD("m_moveGeneration=%d > generation=%d",
+ m_moveGeneration, generation);
+ releaseFrameCache(newCache);
+ return false; // short-circuit if a newer move has already been generated
+ }
+ // if the nav cache has been rebuilt since this focus request was generated,
+ // send a request back to the UI side to recompute the kit-side focus
+ if (m_buildGeneration > buildGeneration || (node && !m_mainFrame->getCacheBuilder().validNode(frame, node))) {
+ DBG_NAV_LOGD("m_buildGeneration=%d > buildGeneration=%d",
+ m_buildGeneration, buildGeneration);
+ gRecomputeFocusMutex.lock();
+ bool first = !m_recomputeEvents.size();
+ m_recomputeEvents.append(generation);
+ gRecomputeFocusMutex.unlock();
+ releaseFrameCache(newCache);
+ if (first)
+ sendRecomputeFocus();
+ return false;
+ }
+ releaseFrameCache(newCache);
+ if (!node && ignoreNullFocus)
+ return true;
+ finalKitFocus(frame, node, x, y);
+ return true;
+}
+
+bool WebViewCore::finalKitFocus(WebCore::Frame* frame, WebCore::Node* node,
+ int x, int y)
+{
+ if (!frame)
+ frame = m_mainFrame;
+ WebCore::CacheBuilder& builder = m_mainFrame->getCacheBuilder();
+ WebCore::Node* oldFocusNode = builder.currentFocus();
+ // 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.
+ bool valid = builder.validNode(frame, node);
+ if (valid) {
+ WebCore::PlatformMouseEvent mouseEvent(m_mousePos, m_mousePos, WebCore::NoButton,
+ WebCore::MouseEventMoved, 1, false, false, false, false, WebCore::currentTime());
+ frame->eventHandler()->handleMouseMoveEvent(mouseEvent);
+ }
+ WebCore::Document* oldDoc = oldFocusNode ? oldFocusNode->document() : 0;
+ if (!node) {
+ if (oldFocusNode)
+ oldDoc->setFocusedNode(0);
+ return false;
+ } else if (!valid) {
+ DBG_NAV_LOGD("sendMarkNodeInvalid node=%p", node);
+ sendMarkNodeInvalid(node);
+ if (oldFocusNode)
+ oldDoc->setFocusedNode(0);
+ return false;
+ }
+ // If we jump frames (docs), kill the focus on the old doc
+ builder.setLastFocus(node);
+ if (oldFocusNode && node->document() != oldDoc) {
+ oldDoc->setFocusedNode(0);
+ }
+ if (!node->isTextNode())
+ static_cast<WebCore::Element*>(node)->focus(false);
+ //setFocus on things that WebCore doesn't recognize as supporting focus
+ //for instance, if there is an onclick element that does not support focus
+ DBG_NAV_LOGD("setFocusedNode node=%p", node);
+ node->document()->setFocusedNode(node);
+ m_lastFocused = node;
+ m_lastFocusedBounds = node->getRect();
+ return true;
+}
+
+// helper function to find the frame that has focus
+static WebCore::Frame* FocusedFrame(WebCore::Frame* frame)
+{
+ if (!frame)
+ return 0;
+ WebCore::Node* focusNode = frame->getCacheBuilder().currentFocus();
+ if (!focusNode)
+ return 0;
+ WebCore::Document* doc = focusNode->document();
+ if (!doc)
+ return 0;
+ return doc->frame();
+}
+
+static WebCore::RenderTextControl* FocusedTextControl(WebCore::Frame* frame)
+{
+ WebCore::Node* focusNode = frame->getCacheBuilder().currentFocus();
+ WebCore::RenderObject* renderer = focusNode->renderer();
+ if (renderer && (renderer->isTextField() || renderer->isTextArea())) {
+ return static_cast<WebCore::RenderTextControl*>(renderer);
+ }
+ return 0;
+}
+
+WebCore::Frame* WebViewCore::changedKitFocus(WebCore::Frame* frame,
+ WebCore::Node* node, int x, int y)
+{
+ if (!frame || !node)
+ return m_mainFrame;
+ WebCore::Node* current = m_mainFrame->getCacheBuilder().currentFocus();
+ if (current == node)
+ return frame;
+ return finalKitFocus(frame, node, x, y) ? frame : m_mainFrame;
+}
+
+static int findTextBoxIndex(WebCore::Node* node, const WebCore::IntPoint& pt)
+{
+ if (!node->isTextNode()) {
+ DBG_NAV_LOGD("node=%p pt=(%d,%d) isText=false", node, pt.x(), pt.y());
+ return -2; // error
+ }
+ WebCore::RenderText* renderText = (WebCore::RenderText*) node->renderer();
+ if (!renderText) {
+ DBG_NAV_LOGD("node=%p pt=(%d,%d) renderText=0", node, pt.x(), pt.y());
+ return -3; // error
+ }
+ int renderX, renderY;
+ renderText->absolutePosition(renderX, renderY);
+ WebCore::InlineTextBox *textBox = renderText->firstTextBox();
+ int globalX, globalY;
+ WebCore::CacheBuilder::GetGlobalOffset(node, &globalX, &globalY);
+ int x = pt.x() - globalX;
+ int y = pt.y() - globalY;
+ do {
+ int textBoxStart = textBox->start();
+ int textBoxEnd = textBoxStart + textBox->len();
+ if (textBoxEnd <= textBoxStart)
+ continue;
+ WebCore::IntRect bounds = textBox->selectionRect(renderX, renderY,
+ textBoxStart, textBoxEnd);
+ if (!bounds.contains(x, y))
+ continue;
+ int offset = textBox->offsetForPosition(x - renderX);
+#if DEBUG_NAV_UI
+ int prior = offset > 0 ? textBox->positionForOffset(offset - 1) : -1;
+ int current = textBox->positionForOffset(offset);
+ int next = textBox->positionForOffset(offset + 1);
+ DBG_NAV_LOGD(
+ "offset=%d pt.x=%d globalX=%d renderX=%d x=%d "
+ "textBox->x()=%d textBox->start()=%d prior=%d current=%d next=%d",
+ offset, pt.x(), globalX, renderX, x,
+ textBox->xPos(), textBox->start(), prior, current, next
+ );
+#endif
+ return textBox->start() + offset;
+ } while ((textBox = textBox->nextTextBox()));
+ return -1; // couldn't find point, may have walked off end
+}
+
+static inline bool isPunctuation(UChar c)
+{
+ return WTF::Unicode::category(c) & (0
+ | WTF::Unicode::Punctuation_Dash
+ | WTF::Unicode::Punctuation_Open
+ | WTF::Unicode::Punctuation_Close
+ | WTF::Unicode::Punctuation_Connector
+ | WTF::Unicode::Punctuation_Other
+ | WTF::Unicode::Punctuation_InitialQuote
+ | WTF::Unicode::Punctuation_FinalQuote
+ );
+}
+
+static int centerX(const SkIRect& rect)
+{
+ return (rect.fLeft + rect.fRight) >> 1;
+}
+
+static int centerY(const SkIRect& rect)
+{
+ return (rect.fTop + rect.fBottom) >> 1;
+}
+
+WebCore::String WebViewCore::getSelection(SkRegion* selRgn)
+{
+ SkRegion::Iterator iter(*selRgn);
+ // FIXME: switch this to use StringBuilder instead
+ WebCore::String result;
+ WebCore::Node* lastStartNode = 0;
+ int lastStartEnd = -1;
+ UChar lastChar = 0xffff;
+ for (; !iter.done(); iter.next()) {
+ const SkIRect& rect = iter.rect();
+ DBG_NAV_LOGD("rect=(%d, %d, %d, %d)", rect.fLeft, rect.fTop,
+ rect.fRight, rect.fBottom);
+ int cy = centerY(rect);
+ WebCore::IntPoint startPt = WebCore::IntPoint(rect.fLeft + 1, cy);
+ WebCore::HitTestResult hitTestResult = m_mainFrame->eventHandler()->
+ hitTestResultAtPoint(startPt, false);
+ WebCore::Node* node = hitTestResult.innerNode();
+ if (!node) {
+ DBG_NAV_LOG("!node");
+ return result;
+ }
+ WebCore::IntPoint endPt = WebCore::IntPoint(rect.fRight - 2, cy);
+ hitTestResult = m_mainFrame->eventHandler()->hitTestResultAtPoint(endPt, false);
+ WebCore::Node* endNode = hitTestResult.innerNode();
+ if (!endNode) {
+ DBG_NAV_LOG("!endNode");
+ return result;
+ }
+ int start = findTextBoxIndex(node, startPt);
+ if (start < 0)
+ continue;
+ int end = findTextBoxIndex(endNode, endPt);
+ if (end < -1) // use node if endNode is not valid
+ endNode = node;
+ if (end <= 0)
+ end = static_cast<WebCore::Text*>(endNode)->string()->length();
+ DBG_NAV_LOGD("node=%p start=%d endNode=%p end=%d", node, start, endNode, end);
+ WebCore::Node* startNode = node;
+ do {
+ if (!node->isTextNode())
+ continue;
+ if (node->getRect().isEmpty())
+ continue;
+ WebCore::Text* textNode = static_cast<WebCore::Text*>(node);
+ WebCore::StringImpl* string = textNode->string();
+ if (!string->length())
+ continue;
+ const UChar* chars = string->characters();
+ int newLength = node == endNode ? end : string->length();
+ if (node == startNode) {
+ #if DEBUG_NAV_UI
+ if (node == lastStartNode)
+ DBG_NAV_LOGD("start=%d last=%d", start, lastStartEnd);
+ #endif
+ if (node == lastStartNode && start < lastStartEnd)
+ break; // skip rect if text overlaps already written text
+ lastStartNode = node;
+ lastStartEnd = newLength - start;
+ }
+ if (newLength < start) {
+ DBG_NAV_LOGD("newLen=%d < start=%d", newLength, start);
+ break;
+ }
+ if (!isPunctuation(chars[start]))
+ result.append(' ');
+ result.append(chars + start, newLength - start);
+ start = 0;
+ } while (node != endNode && (node = node->traverseNextNode()));
+ }
+ result = result.simplifyWhiteSpace().stripWhiteSpace();
+#if DUMP_NAV_CACHE
+ {
+ char buffer[256];
+ WebCore::CacheBuilder::Debug debug;
+ debug.init(buffer, sizeof(buffer));
+ debug.print("copy: ");
+ debug.wideString(result);
+ DUMP_NAV_LOGD("%s", buffer);
+ }
+#endif
+ return result;
+}
+
+static void selectInFrame(WebCore::Frame* frame, int start, int end)
+{
+ WebCore::Document* doc = frame->document();
+ if (!doc)
+ return;
+
+ WebCore::Node* focus = doc->focusedNode();
+ if (!focus)
+ return;
+
+ WebCore::RenderObject* renderer = focus->renderer();
+ if (renderer && (renderer->isTextField() || renderer->isTextArea())) {
+ WebCore::RenderTextControl* rtc = static_cast<WebCore::RenderTextControl*>(renderer);
+ if (start > end) {
+ int temp = start;
+ start = end;
+ end = temp;
+ }
+ rtc->setSelectionRange(start, end);
+ frame->revealSelection();
+ }
+}
+
+WebCore::Frame* WebViewCore::setSelection(WebCore::Frame* frame, WebCore::Node* node,
+ int x, int y, int start, int end)
+{
+ // FIXME: Consider using a generation number to avoid doing this many more times than necessary.
+ frame = changedKitFocus(frame, node, x, y);
+ if (!frame)
+ return 0;
+ selectInFrame(frame, start, end);
+ return frame;
+}
+
+// Shortcut for no modifier keys
+#define NO_MODIFIER_KEYS (static_cast<WebCore::PlatformKeyboardEvent::ModifierKey>(0))
+
+WebCore::Frame* WebViewCore::deleteSelection(WebCore::Frame* frame, WebCore::Node* node,
+ int x, int y, int start, int end)
+{
+ frame = setSelection(frame, node, x, y, start, end);
+ if (start != end) {
+ WebCore::PlatformKeyboardEvent downEvent(kKeyCodeDel, WebCore::VK_BACK,
+ WebCore::PlatformKeyboardEvent::KeyDown, 0, NO_MODIFIER_KEYS);
+ frame->eventHandler()->keyEvent(downEvent);
+ WebCore::PlatformKeyboardEvent upEvent(kKeyCodeDel, WebCore::VK_BACK,
+ WebCore::PlatformKeyboardEvent::KeyUp, 0, NO_MODIFIER_KEYS);
+ frame->eventHandler()->keyEvent(upEvent);
+ }
+ return frame;
+}
+
+void WebViewCore::replaceTextfieldText(WebCore::Frame* frame, WebCore::Node* node, int x, int y,
+ int oldStart, int oldEnd, jstring replace, int start, int end)
+{
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+
+ WebCore::String webcoreString = to_string(env, replace);
+ frame = setSelection(frame, node, x, y, oldStart, oldEnd);
+ WebCore::TypingCommand::insertText(frame->document(), webcoreString, false);
+ selectInFrame(frame, start, end);
+}
+
+void WebViewCore::passToJs(WebCore::Frame* frame, WebCore::Node* node, int x, int y, int generation,
+ jstring currentText, int keyCode, int keyValue, bool down, bool cap, bool fn, bool sym)
+{
+ frame = changedKitFocus(frame, node, x, y);
+ // Construct the ModifierKey value
+ int mods = 0;
+ if (cap) {
+ mods |= WebCore::PlatformKeyboardEvent::ShiftKey;
+ }
+ if (fn) {
+ mods |= WebCore::PlatformKeyboardEvent::AltKey;
+ }
+ if (sym) {
+ mods |= WebCore::PlatformKeyboardEvent::CtrlKey;
+ }
+ WebCore::PlatformKeyboardEvent event(keyCode, keyValue,
+ down ? WebCore::PlatformKeyboardEvent::KeyDown :
+ WebCore::PlatformKeyboardEvent::KeyUp,
+ 0, static_cast<WebCore::PlatformKeyboardEvent::ModifierKey>(mods));
+ // Block text field updates during a key press.
+ m_blockTextfieldUpdates = true;
+ frame->eventHandler()->keyEvent(event);
+ m_blockTextfieldUpdates = false;
+ m_textGeneration = generation;
+
+ WebCore::Node* currentFocus = m_mainFrame->getCacheBuilder().currentFocus();
+ // Make sure we have the same focus and it is a text field.
+ if (node == currentFocus && currentFocus) {
+ WebCore::RenderObject* renderer = currentFocus->renderer();
+ if (renderer && (renderer->isTextField() || renderer->isTextArea())) {
+ WebCore::RenderTextControl* renderText = static_cast<WebCore::RenderTextControl*>(renderer);
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ WebCore::String current = to_string(env, currentText);
+ WebCore::String test = renderText->text();
+ // If the text changed during the key event, update the UI text field.
+ if (test != current)
+ updateTextfield(currentFocus, false, test);
+ }
+ }
+}
+
+void WebViewCore::saveDocumentState(WebCore::Frame* frame, WebCore::Node* node, int x, int y)
+{
+ frame = changedKitFocus(frame, node, x, y);
+ WebCore::HistoryItem *item = frame->loader()->currentHistoryItem();
+
+ // 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());
+ }
+}
+
+// Convert a WebCore::String into an array of characters where the first
+// character represents the length, for easy conversion to java.
+static uint16_t* stringConverter(const WebCore::String& text)
+{
+ size_t length = text.length();
+ uint16_t* itemName = new uint16_t[length+1];
+ itemName[0] = (uint16_t)length;
+ uint16_t* firstChar = &(itemName[1]);
+ memcpy((void*)firstChar, text.characters(), sizeof(UChar)*length);
+ return itemName;
+}
+
+// Response to dropdown created for a listbox.
+class ListBoxReply : public WebCoreReply {
+public:
+ ListBoxReply(WebCore::HTMLSelectElement* select, WebCore::Frame* frame, WebViewCore* view)
+ : m_select(select)
+ , m_frame(frame)
+ , m_viewImpl(view)
+ {}
+
+ // Response used if the listbox only allows single selection.
+ // index is listIndex of the selected item, or -1 if nothing is selected.
+ virtual void replyInt(int index)
+ {
+ // If the select element no longer exists, do to a page change, etc, silently return.
+ if (!m_select || !m_viewImpl->m_mainFrame->getCacheBuilder().validNode(m_frame, m_select))
+ return;
+ if (-1 == index) {
+ if (m_select->selectedIndex() != -1) {
+#ifdef ANDROID_DESELECT_SELECT
+ m_select->deselectItems();
+#endif
+ m_select->onChange();
+ m_viewImpl->contentInvalidate(m_select->getRect());
+ }
+ return;
+ }
+ WebCore::HTMLOptionElement* option = static_cast<WebCore::HTMLOptionElement*>(
+ m_select->item(m_select->listToOptionIndex(index)));
+ if (!option->selected()) {
+ option->setSelected(true);
+ m_select->onChange();
+ m_viewImpl->contentInvalidate(m_select->getRect());
+ }
+ }
+
+ // Response if the listbox allows multiple selection. array stores the listIndices
+ // of selected positions.
+ virtual void replyIntArray(const int* array, int count)
+ {
+ // If the select element no longer exists, do to a page change, etc, silently return.
+ if (!m_select || !m_viewImpl->m_mainFrame->getCacheBuilder().validNode(m_frame, m_select))
+ return;
+#ifdef ANDROID_DESELECT_SELECT
+ m_select->deselectItems();
+#endif
+ WebCore::HTMLOptionElement* option;
+ for (int i = 0; i < count; i++) {
+ option = static_cast<WebCore::HTMLOptionElement*>(
+ m_select->item(array[m_select->listToOptionIndex(i)]));
+ option->setSelected(true);
+ }
+ m_viewImpl->contentInvalidate(m_select->getRect());
+ }
+private:
+ // The select element associated with this listbox.
+ WebCore::HTMLSelectElement* m_select;
+ // The frame of this select element, to verify that it is valid.
+ WebCore::Frame* m_frame;
+ // For calling invalidate and checking the select element's validity
+ WebViewCore* m_viewImpl;
+};
+
+// 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::listBoxRequest(WebCoreReply* reply, const uint16_t** labels, size_t count, const int enabled[], size_t enabledCount,
+ bool multiple, const int selected[], size_t selectedCountOrSelection)
+{
+ // Reuse m_popupReply
+ Release(m_popupReply);
+ m_popupReply = 0;
+
+ 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 = javavm_to_jnienv(m_javaGlue->m_JVM);
+ jobjectArray labelArray = makeLabelArray(env, labels, count);
+
+ // Create an array determining whether each item is enabled.
+ jbooleanArray enabledArray = env->NewBooleanArray(enabledCount);
+ checkException(env);
+ jboolean* ptrArray = env->GetBooleanArrayElements(enabledArray, 0);
+ checkException(env);
+ for (size_t i = 0; i < enabledCount; i++) {
+ ptrArray[i] = enabled[i];
+ }
+ env->ReleaseBooleanArrayElements(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::keyUp(KeyCode keyCode, int keyVal)
+{
+ DBG_NAV_LOGD("keyCode=%d", keyCode);
+ bool keyHandled = false;
+ WebCore::Node* focusNode = m_mainFrame->getCacheBuilder().currentFocus();
+ if (focusNode) {
+ WebCore::Frame* focusFrame = focusNode->document()->frame();
+ switch (keyCode) {
+ case kKeyCodeNewline:
+ case kKeyCodeDpadCenter: {
+ focusFrame->loader()->resetMultipleFormSubmissionProtection();
+ WebFrame* webFrame = WebFrame::getWebFrame(m_mainFrame);
+ webFrame->setUserInitiatedClick(true);
+ if ((focusNode->hasTagName(WebCore::HTMLNames::inputTag) &&
+ ((WebCore::HTMLInputElement*)focusNode)->isTextField()) ||
+ focusNode->hasTagName(WebCore::HTMLNames::textareaTag)) {
+ // Create the key down event.
+ WebCore::PlatformKeyboardEvent keydown(keyCode, keyVal,
+ WebCore::PlatformKeyboardEvent::KeyDown, 0,
+ NO_MODIFIER_KEYS);
+ // Create the key up event.
+ WebCore::PlatformKeyboardEvent keyup(keyCode, keyVal,
+ WebCore::PlatformKeyboardEvent::KeyUp, 0,
+ NO_MODIFIER_KEYS);
+ // Send both events.
+ keyHandled = focusFrame->eventHandler()->keyEvent(keydown);
+ keyHandled |= focusFrame->eventHandler()->keyEvent(keyup);
+ } else {
+ keyHandled = handleMouseClick(focusFrame, focusNode);
+ }
+ webFrame->setUserInitiatedClick(false);
+ break;
+ }
+ default:
+ keyHandled = false;
+ }
+ }
+ m_blockFocusChange = false;
+ return keyHandled;
+}
+
+bool WebViewCore::sendKeyToFocusNode(int keyCode, UChar32 unichar,
+ int repeatCount, bool isShift, bool isAlt, KeyAction action) {
+ WebCore::Node* focusNode = m_mainFrame->getCacheBuilder().currentFocus();
+ if (focusNode) {
+ WebCore::Frame* focusFrame = focusNode->document()->frame();
+ WebCore::PlatformKeyboardEvent::Type type;
+ switch (action) {
+ case DownKeyAction:
+ type = WebCore::PlatformKeyboardEvent::KeyDown;
+ break;
+ case UpKeyAction:
+ type = WebCore::PlatformKeyboardEvent::KeyUp;
+ break;
+ default:
+ SkDebugf("---- unexpected KeyAction %d\n", action);
+ return false;
+ }
+
+ int mods = 0; // PlatformKeyboardEvent::ModifierKey
+ if (isShift) {
+ mods |= WebCore::PlatformKeyboardEvent::ShiftKey;
+ }
+ if (isAlt) {
+ mods |= WebCore::PlatformKeyboardEvent::AltKey;
+ }
+
+ WebCore::PlatformKeyboardEvent evt(keyCode, unichar, type, repeatCount,
+ static_cast<WebCore::PlatformKeyboardEvent::ModifierKey>(mods));
+ return focusFrame->eventHandler()->keyEvent(evt);
+ }
+ return false;
+}
+
+bool WebViewCore::handleTouchEvent(int action, int x, int y)
+{
+ bool preventDefault = false;
+
+#if ENABLE(TOUCH_EVENTS) // Android
+ WebCore::TouchEventType type = WebCore::TouchEventCancel;
+ switch (action) {
+ case 0: // MotionEvent.ACTION_DOWN
+ type = WebCore::TouchEventStart;
+ break;
+ case 1: // MotionEvent.ACTION_UP
+ type = WebCore::TouchEventEnd;
+ break;
+ case 2: // MotionEvent.ACTION_MOVE
+ type = WebCore::TouchEventMove;
+ break;
+ case 3: // MotionEvent.ACTION_CANCEL
+ type = WebCore::TouchEventCancel;
+ break;
+ }
+ WebCore::IntPoint pt(x - m_scrollOffsetX, y - m_scrollOffsetY);
+ WebCore::PlatformTouchEvent te(pt, pt, type);
+ preventDefault = m_mainFrame->eventHandler()->handleTouchEvent(te);
+#endif
+
+ return preventDefault;
+}
+
+void WebViewCore::touchUp(int touchGeneration, int buildGeneration,
+ WebCore::Frame* frame, WebCore::Node* node, int x, int y, int size,
+ bool isClick, bool retry)
+{
+ 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
+ }
+ if (retry)
+ finalKitFocus(frame, node, x, y);
+ else if (!commonKitFocus(touchGeneration, buildGeneration,
+ frame, node, x, y, false)) {
+ return;
+ }
+ m_lastGeneration = touchGeneration;
+ // If this is just a touch and not a click, we have already done the change in focus,
+ // so just leave the function now.
+ if (!isClick)
+ return;
+ if (frame) {
+ frame->loader()->resetMultipleFormSubmissionProtection();
+ }
+ WebCore::EditorClientAndroid* client = static_cast<WebCore::EditorClientAndroid*>(m_mainFrame->editor()->client());
+ client->setFromClick(true);
+ DBG_NAV_LOGD("touchGeneration=%d handleMouseClick frame=%p node=%p"
+ " x=%d y=%d", touchGeneration, frame, node, x, y);
+ handleMouseClick(frame, node);
+ client->setFromClick(false);
+}
+
+bool WebViewCore::handleMouseClick(WebCore::Frame* framePtr, WebCore::Node* nodePtr)
+{
+ if (framePtr && !m_mainFrame->getCacheBuilder().validNode(framePtr, nodePtr))
+ return false;
+ WebFrame* webFrame = WebFrame::getWebFrame(m_mainFrame);
+ // 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 && nodePtr->hasTagName(WebCore::HTMLNames::areaTag)) {
+ webFrame->setUserInitiatedClick(true);
+ WebCore::EventTargetNodeCast(nodePtr)->dispatchSimulatedClick(0, true, true);
+ webFrame->setUserInitiatedClick(false);
+ return true;
+ }
+ WebCore::RenderObject* renderer = nodePtr ? nodePtr->renderer() : 0;
+ if (renderer) {
+ if (renderer->isMenuList()) {
+ WebCore::HTMLSelectElement* select = static_cast<WebCore::HTMLSelectElement*>(nodePtr);
+ const WTF::Vector<WebCore::HTMLElement*>& listItems = select->listItems();
+ SkTDArray<const uint16_t*> names;
+ SkTDArray<int> enabledArray;
+ SkTDArray<int> selectedArray;
+ int size = listItems.size();
+ bool multiple = select->multiple();
+ for (int i = 0; i < size; i++) {
+ if (listItems[i]->hasTagName(WebCore::HTMLNames::optionTag)) {
+ WebCore::HTMLOptionElement* option = static_cast<WebCore::HTMLOptionElement*>(listItems[i]);
+ *names.append() = stringConverter(option->optionText());
+ *enabledArray.append() = option->disabled() ? 0 : 1;
+ if (multiple && option->selected())
+ *selectedArray.append() = i;
+ } else if (listItems[i]->hasTagName(WebCore::HTMLNames::optgroupTag)) {
+ WebCore::HTMLOptGroupElement* optGroup = static_cast<WebCore::HTMLOptGroupElement*>(listItems[i]);
+ *names.append() = stringConverter(optGroup->groupLabelText());
+ *enabledArray.append() = 0;
+ if (multiple)
+ *selectedArray.append() = 0;
+ }
+ }
+ WebCoreReply* reply = new ListBoxReply(select, select->document()->frame(), this);
+ listBoxRequest(reply, names.begin(), size, enabledArray.begin(), enabledArray.count(),
+ multiple, selectedArray.begin(), multiple ? selectedArray.count() :
+ select->optionToListIndex(select->selectedIndex()));
+ return true;
+ }
+ }
+ if (!framePtr)
+ framePtr = m_mainFrame;
+ webFrame->setUserInitiatedClick(true);
+ DBG_NAV_LOGD("m_mousePos={%d,%d}", m_mousePos.x(), m_mousePos.y());
+ WebCore::PlatformMouseEvent mouseDown(m_mousePos, m_mousePos, WebCore::LeftButton,
+ WebCore::MouseEventPressed, 1, false, false, false, false,
+ WebCore::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,
+ WebCore::currentTime());
+ bool handled = framePtr->eventHandler()->handleMouseReleaseEvent(mouseUp);
+ webFrame->setUserInitiatedClick(false);
+ 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 = NULL;
+ }
+}
+
+void WebViewCore::jsAlert(const WebCore::String& url, const WebCore::String& text)
+{
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ jstring jInputStr = env->NewString((unsigned short *)text.characters(), text.length());
+ jstring jUrlStr = env->NewString((unsigned short *)url.characters(), url.length());
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsAlert, jUrlStr, jInputStr);
+ env->DeleteLocalRef(jInputStr);
+ env->DeleteLocalRef(jUrlStr);
+ checkException(env);
+}
+
+bool WebViewCore::jsConfirm(const WebCore::String& url, const WebCore::String& text)
+{
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ jstring jInputStr = env->NewString((unsigned short *)text.characters(), text.length());
+ jstring jUrlStr = env->NewString((unsigned short *)url.characters(), url.length());
+ 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 WebCore::String& url, const WebCore::String& text, const WebCore::String& defaultValue, WebCore::String& result)
+{
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ jstring jInputStr = env->NewString((unsigned short *)text.characters(), text.length());
+ jstring jDefaultStr = env->NewString((unsigned short *)defaultValue.characters(), defaultValue.length());
+ jstring jUrlStr = env->NewString((unsigned short *)url.characters(), url.length());
+ jstring returnVal = (jstring) env->CallObjectMethod(m_javaGlue->object(env).get(), m_javaGlue->m_jsPrompt, jUrlStr, jInputStr, jDefaultStr);
+ // If returnVal is null, it means that the user cancelled the dialog.
+ if (!returnVal)
+ return false;
+
+ result = to_string(env, returnVal);
+ env->DeleteLocalRef(jInputStr);
+ env->DeleteLocalRef(jDefaultStr);
+ env->DeleteLocalRef(jUrlStr);
+ checkException(env);
+ return true;
+}
+
+bool WebViewCore::jsUnload(const WebCore::String& url, const WebCore::String& message)
+{
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ jstring jInputStr = env->NewString((unsigned short *)message.characters(), message.length());
+ jstring jUrlStr = env->NewString((unsigned short *)url.characters(), url.length());
+ 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;
+}
+
+AutoJObject
+WebViewCore::getJavaObject()
+{
+ return getRealObject(javavm_to_jnienv(m_javaGlue->m_JVM), m_javaGlue->m_obj);
+}
+
+jobject
+WebViewCore::getWebViewJavaObject()
+{
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ return env->GetObjectField(m_javaGlue->object(env).get(), gWebViewCoreFields.m_webView);
+}
+
+void WebViewCore::updateTextfield(WebCore::Node* ptr, bool changeToPassword,
+ const WebCore::String& text)
+{
+ if (m_blockTextfieldUpdates)
+ return;
+ JNIEnv* env = javavm_to_jnienv(m_javaGlue->m_JVM);
+ if (changeToPassword) {
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_updateTextfield,
+ (int) ptr, true, 0, m_textGeneration);
+ checkException(env);
+ return;
+ }
+ int length = text.length();
+ jstring string = env->NewString((unsigned short *) text.characters(), length);
+ env->CallVoidMethod(m_javaGlue->object(env).get(), m_javaGlue->m_updateTextfield,
+ (int) ptr, false, string, m_textGeneration);
+ env->DeleteLocalRef(string);
+ checkException(env);
+}
+
+void WebViewCore::setSnapAnchor(int x, int y)
+{
+ m_snapAnchorNode = 0;
+ if (!x && !y) {
+ return;
+ }
+
+ WebCore::IntPoint point = WebCore::IntPoint(x, y);
+ WebCore::Node* node = m_mainFrame->eventHandler()->hitTestResultAtPoint(point, false).innerNode();
+ if (node) {
+// LOGD("found focus node name: %s, type %d\n", node->nodeName().utf8().data(), node->nodeType());
+ while (node) {
+ if (node->hasTagName(WebCore::HTMLNames::divTag) ||
+ node->hasTagName(WebCore::HTMLNames::tableTag)) {
+ m_snapAnchorNode = node;
+ return;
+ }
+// LOGD("parent node name: %s, type %d\n", node->nodeName().utf8().data(), node->nodeType());
+ node = node->parentNode();
+ }
+ }
+}
+
+void WebViewCore::snapToAnchor()
+{
+ if (m_snapAnchorNode) {
+ if (m_snapAnchorNode->inDocument()) {
+ int rx, ry;
+ m_snapAnchorNode->renderer()->absolutePosition(rx, ry);
+ scrollTo(rx, ry);
+ } else {
+ m_snapAnchorNode = 0;
+ }
+ }
+}
+
+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);
+}
+
+//----------------------------------------------------------------------
+// Native JNI methods
+//----------------------------------------------------------------------
+static jstring WebCoreStringToJString(JNIEnv *env, WebCore::String string)
+{
+ int length = string.length();
+ if (!length)
+ return 0;
+ jstring ret = env->NewString((jchar *)string.characters(), length);
+ env->DeleteLocalRef(ret);
+ return ret;
+}
+
+static void SetSize(JNIEnv *env, jobject obj, jint width, jint height,
+ jint screenWidth, jfloat scale)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterWV counter;
+#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");
+ // convert the scale to an int
+ int s = (int) (scale * 100);
+ // a negative value indicates that we should not change the scale
+ if (scale < 0)
+ s = viewImpl->scale();
+
+ viewImpl->setSizeScreenWidthAndScale(width, height, screenWidth, s);
+}
+
+static void SetScrollOffset(JNIEnv *env, jobject obj, jint dx, jint dy)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterWV counter;
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "need viewImpl");
+
+ viewImpl->setScrollOffset(dx, dy);
+}
+
+static void SetGlobalBounds(JNIEnv *env, jobject obj, jint x, jint y, jint h,
+ jint v)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "need viewImpl");
+
+ viewImpl->setGlobalBounds(x, y, h, v);
+}
+
+static jboolean KeyUp(JNIEnv *env, jobject obj, jint key, jint val)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterWV counter;
+#endif
+// LOGV("webviewcore::nativeKeyUp(%u)\n", (unsigned)key);
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in nativeKeyUp");
+ return viewImpl->keyUp((KeyCode)key, val);
+}
+
+static bool SendKeyToFocusNode(JNIEnv *env, jobject jwebviewcore,
+ jint keyCode, jint unichar, jint repeatCount,
+ jboolean isShift, jboolean isAlt, jint action)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterWV counter;
+#endif
+
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, jwebviewcore);
+ LOG_ASSERT(viewImpl, "viewImpl not set in SendKeyToFocusNode");
+ return viewImpl->sendKeyToFocusNode((KeyCode)keyCode, unichar, repeatCount,
+ isShift, isAlt, static_cast<WebViewCore::KeyAction>(action));
+}
+
+static void DeleteSelection(JNIEnv *env, jobject obj,
+ jint frame, jint node, jint x, jint y, jint start, jint end)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterWV counter;
+#endif
+ LOGV("webviewcore::nativeDeleteSelection()\n");
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in nativeDeleteSelection");
+ viewImpl->deleteSelection((WebCore::Frame*) frame, (WebCore::Node*) node,
+ x, y, start, end);
+}
+
+static void SetSelection(JNIEnv *env, jobject obj,
+ jint frame, jint node, jint x, jint y, jint start, jint end)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterWV counter;
+#endif
+ LOGV("webviewcore::nativeSetSelection()\n");
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in nativeDeleteSelection");
+ viewImpl->setSelection((WebCore::Frame*) frame, (WebCore::Node*) node,
+ x, y, start, end);
+}
+
+
+static void ReplaceTextfieldText(JNIEnv *env, jobject obj,
+ jint framePtr, jint nodePtr, jint x, jint y, jint oldStart, jint oldEnd,
+ jstring replace, jint start, jint end)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterWV counter;
+#endif
+ LOGV("webviewcore::nativeReplaceTextfieldText()\n");
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in nativeReplaceTextfieldText");
+ viewImpl->replaceTextfieldText((WebCore::Frame*) framePtr, (WebCore::Node*) nodePtr, x, y, oldStart,
+ oldEnd, replace, start, end);
+}
+
+static void PassToJs(JNIEnv *env, jobject obj, jint frame, jint node,
+ jint x, jint y, jint generation, jstring currentText, jint keyCode,
+ jint keyValue, jboolean down, jboolean cap, jboolean fn, jboolean sym)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterWV counter;
+#endif
+ LOGV("webviewcore::nativePassToJs()\n");
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in nativePassToJs");
+ viewImpl->passToJs((WebCore::Frame*) frame, (WebCore::Node*) node,
+ x, y, generation, currentText, keyCode, keyValue, down, cap, fn, sym);
+}
+
+static void SaveDocumentState(JNIEnv *env, jobject obj, jint frame, jint node, jint x, jint y)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterWV counter;
+#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, (WebCore::Node*) node, x, y);
+}
+
+static bool RecordContent(JNIEnv *env, jobject obj, jobject region, jobject pt)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterWV counter;
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ SkRegion* nativeRegion = GraphicsJNI::getNativeRegion(env, region);
+ SkIPoint nativePt;
+ bool result = viewImpl->recordContent(nativeRegion, &nativePt);
+ GraphicsJNI::ipoint_to_jpoint(nativePt, env, pt);
+ return result;
+}
+
+static void SplitContent(JNIEnv *env, jobject obj)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ viewImpl->splitContent();
+}
+
+static void SendListBoxChoice(JNIEnv* env, jobject obj, jint choice)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterWV counter;
+#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
+ TimeCounterWV counter;
+#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)
+{
+ 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 = WebCore::CacheBuilder::FindAddress(addrChars, length,
+ &start, &end) == WebCore::CacheBuilder::FOUND_COMPLETE;
+ jstring ret = 0;
+ if (success) {
+ ret = env->NewString((jchar*) addrChars + start, end - start);
+ env->DeleteLocalRef(ret);
+ }
+ env->ReleaseStringChars(addr, addrChars);
+ return ret;
+}
+
+static jboolean HandleTouchEvent(JNIEnv *env, jobject obj, jint action, jint x, jint y)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ return viewImpl->handleTouchEvent(action, x, y);
+}
+
+static void TouchUp(JNIEnv *env, jobject obj, jint touchGeneration,
+ jint buildGeneration, jint frame, jint node, jint x, jint y, jint size,
+ jboolean isClick, jboolean retry)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterWV counter;
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ viewImpl->touchUp(touchGeneration, buildGeneration,
+ (WebCore::Frame*) frame, (WebCore::Node*) node, x, y, size, isClick, retry);
+}
+
+static jstring RetrieveHref(JNIEnv *env, jobject obj, jint frame,
+ jint node)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ WebCore::String result = viewImpl->retrieveHref((WebCore::Frame*) frame,
+ (WebCore::Node*) node);
+ if (!result.isEmpty())
+ return WebCoreStringToJString(env, result);
+ return 0;
+}
+
+static void SetFinalFocus(JNIEnv *env, jobject obj, jint frame, jint node,
+ jint x, jint y, jboolean block)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterWV counter;
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ viewImpl->setFinalFocus((WebCore::Frame*) frame, (WebCore::Node*) node, x,
+ y, block);
+}
+
+static void SetKitFocus(JNIEnv *env, jobject obj, jint moveGeneration,
+ jint buildGeneration, jint frame, jint node, jint x, jint y,
+ jboolean ignoreNullFocus)
+{
+#ifdef ANDROID_INSTRUMENT
+ TimeCounterWV counter;
+#endif
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ viewImpl->setKitFocus(moveGeneration, buildGeneration,
+ (WebCore::Frame*) frame, (WebCore::Node*) node, x, y,
+ ignoreNullFocus);
+}
+
+static void UnblockFocus(JNIEnv *env, jobject obj)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ viewImpl->unblockFocus();
+}
+
+static void UpdateFrameCache(JNIEnv *env, jobject obj)
+{
+ 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)
+{
+ 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->minPrefWidth();
+ }
+ }
+ }
+ return 0;
+}
+
+static void SetViewportSettingsFromNative(JNIEnv *env, jobject obj)
+{
+ 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());
+#endif
+}
+
+static void SetSnapAnchor(JNIEnv *env, jobject obj, jint x, jint y)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+
+ viewImpl->setSnapAnchor(x, y);
+}
+
+static void SnapToAnchor(JNIEnv *env, jobject obj)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+
+ viewImpl->snapToAnchor();
+}
+
+static void SetBackgroundColor(JNIEnv *env, jobject obj, jint color)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+
+ viewImpl->setBackgroundColor((SkColor) color);
+}
+
+static jstring GetSelection(JNIEnv *env, jobject obj, jobject selRgn)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ LOG_ASSERT(viewImpl, "viewImpl not set in %s", __FUNCTION__);
+ SkRegion* selectionRegion = GraphicsJNI::getNativeRegion(env, selRgn);
+ WebCore::String result = viewImpl->getSelection(selectionRegion);
+ if (!result.isEmpty())
+ return WebCoreStringToJString(env, result);
+ return 0;
+}
+
+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 RefreshPlugins(JNIEnv *env,
+ jobject obj,
+ jboolean reloadOpenPages)
+{
+ // Refresh the list of plugins, optionally reloading all open
+ // pages.
+ WebCore::refreshPlugins(reloadOpenPages);
+}
+
+static void RegisterURLSchemeAsLocal(JNIEnv* env, jobject obj, jstring scheme) {
+ WebCore::FrameLoader::registerURLSchemeAsLocal(to_string(env, scheme));
+}
+
+static void ClearContent(JNIEnv *env, jobject obj)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ viewImpl->clearContent();
+}
+
+static void CopyContentToPicture(JNIEnv *env, jobject obj, jobject pict)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ if (!viewImpl)
+ return;
+ SkPicture* picture = GraphicsJNI::getNativePicture(env, pict);
+ viewImpl->copyContentToPicture(picture);
+}
+
+static bool DrawContent(JNIEnv *env, jobject obj, jobject canv, jint color)
+{
+ WebViewCore* viewImpl = GET_NATIVE_VIEW(env, obj);
+ SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, canv);
+ return viewImpl->drawContent(canvas, color);
+}
+
+// ----------------------------------------------------------------------------
+
+/*
+ * JNI registration.
+ */
+static JNINativeMethod gJavaWebViewCoreMethods[] = {
+ { "nativeClearContent", "()V",
+ (void*) ClearContent },
+ { "nativeCopyContentToPicture", "(Landroid/graphics/Picture;)V",
+ (void*) CopyContentToPicture },
+ { "nativeDrawContent", "(Landroid/graphics/Canvas;I)Z",
+ (void*) DrawContent } ,
+ { "nativeKeyUp", "(II)Z",
+ (void*) KeyUp },
+ { "nativeSendKeyToFocusNode", "(IIIZZI)Z", (void*) SendKeyToFocusNode },
+ { "nativeSendListBoxChoices", "([ZI)V",
+ (void*) SendListBoxChoices },
+ { "nativeSendListBoxChoice", "(I)V",
+ (void*) SendListBoxChoice },
+ { "nativeSetSize", "(IIIF)V",
+ (void*) SetSize },
+ { "nativeSetScrollOffset", "(II)V",
+ (void*) SetScrollOffset },
+ { "nativeSetGlobalBounds", "(IIII)V",
+ (void*) SetGlobalBounds },
+ { "nativeSetSelection", "(IIIIII)V",
+ (void*) SetSelection } ,
+ { "nativeDeleteSelection", "(IIIIII)V",
+ (void*) DeleteSelection } ,
+ { "nativeReplaceTextfieldText", "(IIIIIILjava/lang/String;II)V",
+ (void*) ReplaceTextfieldText } ,
+ { "passToJs", "(IIIIILjava/lang/String;IIZZZZ)V",
+ (void*) PassToJs } ,
+ { "nativeSaveDocumentState", "(IIII)V",
+ (void*) SaveDocumentState },
+ { "nativeFindAddress", "(Ljava/lang/String;)Ljava/lang/String;",
+ (void*) FindAddress },
+ { "nativeHandleTouchEvent", "(III)Z",
+ (void*) HandleTouchEvent },
+ { "nativeTouchUp", "(IIIIIIIZZ)V",
+ (void*) TouchUp },
+ { "nativeRetrieveHref", "(II)Ljava/lang/String;",
+ (void*) RetrieveHref },
+ { "nativeSetFinalFocus", "(IIIIZ)V",
+ (void*) SetFinalFocus },
+ { "nativeSetKitFocus", "(IIIIIIZ)V",
+ (void*) SetKitFocus },
+ { "nativeUnblockFocus", "()V",
+ (void*) UnblockFocus },
+ { "nativeUpdateFrameCache", "()V",
+ (void*) UpdateFrameCache },
+ { "nativeGetContentMinPrefWidth", "()I",
+ (void*) GetContentMinPrefWidth },
+ { "nativeRecordContent", "(Landroid/graphics/Region;Landroid/graphics/Point;)Z",
+ (void*) RecordContent },
+ { "setViewportSettingsFromNative", "()V",
+ (void*) SetViewportSettingsFromNative },
+ { "nativeSetSnapAnchor", "(II)V",
+ (void*) SetSnapAnchor },
+ { "nativeSnapToAnchor", "()V",
+ (void*) SnapToAnchor },
+ { "nativeSplitContent", "()V",
+ (void*) SplitContent },
+ { "nativeSetBackgroundColor", "(I)V",
+ (void*) SetBackgroundColor },
+ { "nativeGetSelection", "(Landroid/graphics/Region;)Ljava/lang/String;",
+ (void*) GetSelection },
+ { "nativeRefreshPlugins", "(Z)V",
+ (void*) RefreshPlugins },
+ { "nativeRegisterURLSchemeAsLocal", "(Ljava/lang/String;)V",
+ (void*) RegisterURLSchemeAsLocal },
+ { "nativeDumpDomTree", "(Z)V",
+ (void*) DumpDomTree },
+ { "nativeDumpRenderTree", "(Z)V",
+ (void*) DumpRenderTree },
+ { "nativeDumpNavTree", "()V",
+ (void*) DumpNavTree }
+};
+
+int register_webviewcore(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_webView = env->GetFieldID(widget,
+ "mWebView", "Landroid/webkit/WebView;");
+ LOG_ASSERT(gWebViewCoreFields.m_webView,
+ "Unable to find android/webkit/WebViewCore.mWebView");
+
+ return jniRegisterNativeMethods(env, "android/webkit/WebViewCore",
+ gJavaWebViewCoreMethods, NELEM(gJavaWebViewCoreMethods));
+}
+
+} /* namespace android */
diff --git a/WebKit/android/jni/WebViewCore.h b/WebKit/android/jni/WebViewCore.h
new file mode 100644
index 0000000..8a073c2
--- /dev/null
+++ b/WebKit/android/jni/WebViewCore.h
@@ -0,0 +1,415 @@
+/*
+ *
+ * Copyright 2006, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WEBVIEWCORE_H
+#define WEBVIEWCORE_H
+
+#include "CacheBuilder.h"
+#include "CachedHistory.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 <jni.h>
+#include <ui/KeycodeLabels.h>
+
+namespace WebCore {
+ class AtomicString;
+ class Color;
+ class FrameView;
+ class HTMLSelectElement;
+ class RenderPart;
+ class RenderText;
+ class Node;
+ class RenderTextControl;
+ class ScrollView;
+ class TimerBase;
+}
+
+struct PluginWidgetAndroid;
+class SkPicture;
+class SkIRect;
+
+namespace android {
+
+ 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
+
+ /**
+ * 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);
+
+ /**
+ * Scroll to the point x,y relative to the current position.
+ * @param x The relative x position.
+ * @param y The relative y position.
+ */
+ void scrollBy(int x, int y);
+
+ /**
+ * Record the invalid rectangle
+ */
+ void contentInvalidate(const WebCore::IntRect &rect);
+
+ /**
+ * Satisfy any outstanding invalidates, so that the current state
+ * of the DOM is drawn.
+ */
+ void contentDraw();
+
+ // invalidate the view/display, NOT the content/DOM
+ void viewInvalidate(const WebCore::IntRect& rect);
+ void viewInvalidate(const SkIRect& rect);
+
+ /**
+ * Invalidate part of the content that may be offscreen at the moment
+ */
+ void offInvalidate(const WebCore::IntRect &rect);
+
+ /**
+ * Called by webcore when the focus was set after returning to prior page
+ * used to rebuild and display any changes in focus
+ */
+ void notifyFocusSet();
+
+ /**
+ * 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 restore the screen width, which in turn restores
+ * the scale.
+ */
+ void restoreScale(int);
+
+ /**
+ * 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 WebCore::String& text);
+
+ // JavaScript support
+ void jsAlert(const WebCore::String& url, const WebCore::String& text);
+ bool jsConfirm(const WebCore::String& url, const WebCore::String& text);
+ bool jsPrompt(const WebCore::String& url, const WebCore::String& message,
+ const WebCore::String& defaultValue, WebCore::String& result);
+ bool jsUnload(const WebCore::String& url, const WebCore::String& message);
+
+ //
+ // Followings support calls from Java to native WebCore
+ //
+
+ WebCore::String retrieveHref(WebCore::Frame* frame, WebCore::Node* node);
+
+ WebCore::String getSelection(SkRegion* );
+
+ // Create a single picture to represent the drawn DOM (used by navcache)
+ void recordPicture(SkPicture* picture);
+
+ // 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);
+ void setFinalFocus(WebCore::Frame* frame, WebCore::Node* node,
+ int x, int y, bool block);
+ void setKitFocus(int moveGeneration, int buildGeneration,
+ WebCore::Frame* frame, WebCore::Node* node, int x, int y,
+ bool ignoreNullFocus);
+
+ // set the scroll amount that webview.java is currently showing
+ void setScrollOffset(int dx, int dy);
+
+ void setGlobalBounds(int x, int y, int h, int v);
+
+ void setSizeScreenWidthAndScale(int width, int height, int screenWidth, int scale);
+
+ /**
+ * Handle keyDown events from Java.
+ * @param keyCode The key pressed.
+ * @return Whether keyCode was handled by this class.
+ */
+ bool keyUp(KeyCode keyCode, int keyValue);
+
+ // These need to be lock-step with the KEY_ACTION values in
+ // WebViewCore.java
+ enum KeyAction {
+ DownKeyAction = 0,
+ UpKeyAction = 1
+ };
+ /**
+ * Send the key event to the focus node (if there is one). Return true
+ * if there is a node, and it claims to have handled the event.
+ */
+ bool sendKeyToFocusNode(int code, UChar32 unichar, int repeatCount,
+ bool isShift, bool isAlt, KeyAction);
+
+ /**
+ * Handle touch event
+ */
+ bool handleTouchEvent(int action, int x, int y);
+
+ /**
+ * Handle motionUp event from the UI thread (called touchUp in the
+ * WebCore thread).
+ */
+ void touchUp(int touchGeneration, int buildGeneration,
+ WebCore::Frame* frame, WebCore::Node* node, int x, int y,
+ int size, bool isClick, bool retry);
+
+ /**
+ * 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 there is no
+ * focus, or if start == end, silently fail, but set selection to that value.
+ * If start and end are out of order, swap them.
+ * Use the frame, node, x, and y to ensure that the correct node is focused.
+ * Return a frame. Convenience so replaceTextfieldText can use this function.
+ */
+ WebCore::Frame* deleteSelection(WebCore::Frame* frame, WebCore::Node* node, int x,
+ int y,int start, int end);
+
+ /**
+ * Set the selection of the currently focused textfield to (start, end).
+ * If start and end are out of order, swap them.
+ * Use the frame, node, x, and y to ensure that the correct node is focused.
+ * Return a frame. Convenience so deleteSelection can use this function.
+ */
+ WebCore::Frame* setSelection(WebCore::Frame* frame, WebCore::Node* node, int x,
+ int y,int start, int end);
+ /**
+ * In the currenlty focused textfield, represented by frame, node, x, and y (which
+ * are used to ensure it has focus), 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(WebCore::Frame* frame, WebCore::Node* node, int x, int y,
+ int oldStart, int oldEnd, jstring replace, int start, int end);
+ void passToJs(WebCore::Frame* frame, WebCore::Node* node, int x, int y, int generation,
+ jstring currentText, int jKeyCode, int keyVal, bool down, bool cap, bool fn, bool sym);
+
+ void saveDocumentState(WebCore::Frame* frame, WebCore::Node* node, int x, int y);
+
+ // 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 setSnapAnchor(int x, int y);
+ void snapToAnchor();
+ void unblockFocus() { m_blockFocusChange = false; }
+ void updateFrameCache();
+ 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*);
+ void invalPlugin(PluginWidgetAndroid*);
+ void drawPlugins();
+
+ // Notify the Java side whether it needs to pass down the touch events
+ void needTouchEvents(bool);
+
+ // other public functions
+ public:
+ void removeFrameGeneration(WebCore::Frame* );
+ void updateFrameGeneration(WebCore::Frame* );
+
+ // reset the picture set to empty
+ void clearContent();
+
+ // flatten the picture set to a picture
+ void copyContentToPicture(SkPicture* );
+
+ // draw the picture set with the specified background color
+ bool drawContent(SkCanvas* , SkColor );
+
+ // record the inval area, and the picture size
+ bool recordContent(SkRegion* , SkIPoint* );
+ int screenWidth() const { return m_screenWidth; }
+ int scale() const { return m_scale; }
+ WebCore::Frame* mainFrame() const { return m_mainFrame; }
+
+ // utility to split slow parts of the picture set
+ void splitContent();
+
+ // these members are shared with webview.cpp
+ int retrieveFrameGeneration(WebCore::Frame* );
+ static Mutex gFrameCacheMutex;
+ CachedRoot* m_frameCacheKit; // nav data being built by webcore
+ SkPicture* m_navPictureKit;
+ int m_generation; // copy of the number bumped by WebViewNative
+ 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_useReplay;
+ bool m_findIsUp;
+ static Mutex gRecomputeFocusMutex;
+ WTF::Vector<int> m_recomputeEvents;
+ // 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:
+ // 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);
+
+ 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);
+
+ friend class ListBoxReply;
+ struct FrameGen {
+ const WebCore::Frame* m_frame;
+ int m_generation;
+ };
+ WTF::Vector<FrameGen> m_frameGenerations;
+ static Mutex gFrameGenerationMutex;
+ struct JavaGlue;
+ struct JavaGlue* m_javaGlue;
+ WebCore::Frame* m_mainFrame;
+ WebCoreReply* m_popupReply;
+ WebCore::Node* m_lastFocused;
+ WebCore::IntRect m_lastFocusedBounds;
+ static Mutex m_contentMutex; // protects ui/core thread pictureset access
+ PictureSet m_content; // the set of pictures to draw (accessed by UI too)
+ SkRegion m_addInval; // the accumulated inval region (not yet drawn)
+ // Used in passToJS to avoid updating the UI text field until after the
+ // key event has been processed.
+ bool m_blockTextfieldUpdates;
+ 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_buildGeneration;
+ 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_blockFocusChange;
+ int m_lastPassed;
+ int m_lastVelocity;
+ CachedHistory m_history;
+ WebCore::Node* m_snapAnchorNode;
+ int m_screenWidth;
+ int m_scale;
+
+ SkTDArray<PluginWidgetAndroid*> m_plugins;
+ WebCore::Timer<WebViewCore> m_pluginInvalTimer;
+ void pluginInvalTimerFired(WebCore::Timer<WebViewCore>*) {
+ this->drawPlugins();
+ }
+
+ WebCore::Frame* changedKitFocus(WebCore::Frame* frame,
+ WebCore::Node* node, int x, int y);
+ bool commonKitFocus(int generation, int buildGeneration,
+ WebCore::Frame* frame, WebCore::Node* node, int x, int y,
+ bool ignoreNullFocus);
+ bool finalKitFocus(WebCore::Frame* frame, WebCore::Node* node, int x, int y);
+ void doMaxScroll(WebCore::CacheBuilder::Direction dir);
+ SkPicture* rebuildPicture(const SkIRect& inval);
+ void rebuildPictureSet(PictureSet* );
+ void sendMarkNodeInvalid(WebCore::Node* );
+ void sendNotifyFocusSet();
+ void sendNotifyProgressFinished();
+ void sendRecomputeFocus();
+ bool handleMouseClick(WebCore::Frame* framePtr, WebCore::Node* nodePtr);
+ bool prepareFrameCache();
+ void releaseFrameCache(bool newCache);
+#if DEBUG_NAV_UI
+ uint32_t m_now;
+#endif
+ };
+
+} // namespace android
+
+#endif // WEBVIEWCORE_H