summaryrefslogtreecommitdiffstats
path: root/Source/WebKit/android/jni/WebHistory.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebKit/android/jni/WebHistory.cpp')
-rw-r--r--Source/WebKit/android/jni/WebHistory.cpp754
1 files changed, 302 insertions, 452 deletions
diff --git a/Source/WebKit/android/jni/WebHistory.cpp b/Source/WebKit/android/jni/WebHistory.cpp
index b6972e4..aa74b81 100644
--- a/Source/WebKit/android/jni/WebHistory.cpp
+++ b/Source/WebKit/android/jni/WebHistory.cpp
@@ -28,7 +28,6 @@
#include "config.h"
#include "WebHistory.h"
-#include "AndroidLog.h"
#include "BackForwardList.h"
#include "BackForwardListImpl.h"
#include "DocumentLoader.h"
@@ -36,7 +35,6 @@
#include "FrameLoader.h"
#include "FrameLoaderClientAndroid.h"
#include "FrameTree.h"
-#include "GraphicsJNI.h"
#include "HistoryItem.h"
#include "IconDatabase.h"
#include "Page.h"
@@ -56,13 +54,16 @@
namespace android {
// Forward declarations
-static void writeItem(WTF::Vector<char>& vector, WebCore::HistoryItem* item);
-static void writeChildrenRecursive(WTF::Vector<char>& vector, WebCore::HistoryItem* parent);
-static bool readItemRecursive(WebCore::HistoryItem* child, const char** pData, int length);
+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 {
@@ -77,7 +78,7 @@ struct WebBackForwardListFields {
static void WebHistoryClose(JNIEnv* env, jobject obj, jint frame)
{
- ALOG_ASSERT(frame, "Close needs a valid Frame pointer!");
+ LOG_ASSERT(frame, "Close needs a valid Frame pointer!");
WebCore::Frame* pFrame = (WebCore::Frame*)frame;
WebCore::BackForwardListImpl* list = static_cast<WebCore::BackForwardListImpl*>(pFrame->page()->backForwardList());
@@ -144,7 +145,7 @@ static void WebHistoryClose(JNIEnv* env, jobject obj, jint frame)
static void WebHistoryRestoreIndex(JNIEnv* env, jobject obj, jint frame, jint index)
{
- ALOG_ASSERT(frame, "RestoreState needs a valid Frame pointer!");
+ LOG_ASSERT(frame, "RestoreState needs a valid Frame pointer!");
WebCore::Frame* pFrame = (WebCore::Frame*)frame;
WebCore::Page* page = pFrame->page();
WebCore::HistoryItem* currentItem =
@@ -155,10 +156,10 @@ static void WebHistoryRestoreIndex(JNIEnv* env, jobject obj, jint frame, jint in
page->goToItem(currentItem, FrameLoadTypeIndexedBackForward);
}
-static jint WebHistoryInflate(JNIEnv* env, jobject obj, jint frame, jbyteArray data)
+static void WebHistoryInflate(JNIEnv* env, jobject obj, jint frame, jbyteArray data)
{
- ALOG_ASSERT(frame, "Inflate needs a valid frame pointer!");
- ALOG_ASSERT(data, "Inflate needs a valid data pointer!");
+ 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);
@@ -167,7 +168,7 @@ static jint WebHistoryInflate(JNIEnv* env, jobject obj, jint frame, jbyteArray d
// Inflate the history tree into one HistoryItem or null if the inflation
// failed.
RefPtr<WebCore::HistoryItem> newItem = WebCore::HistoryItem::create();
- WebHistoryItem* bridge = new WebHistoryItem(newItem.get());
+ WebHistoryItem* bridge = new WebHistoryItem(env, obj, newItem.get());
newItem->setBridge(bridge);
// Inflate the item recursively. If it fails, that is ok. We'll have an
@@ -177,7 +178,7 @@ static jint WebHistoryInflate(JNIEnv* env, jobject obj, jint frame, jbyteArray d
// 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);
- readItemRecursive(newItem.get(), &ptr, (int)size);
+ read_item_recursive(newItem.get(), &ptr, (int)size);
env->ReleaseByteArrayElements(data, const_cast<jbyte*>(bytes), JNI_ABORT);
bridge->setActive();
@@ -187,110 +188,49 @@ static jint WebHistoryInflate(JNIEnv* env, jobject obj, jint frame, jbyteArray d
// Update the item.
bridge->updateHistoryItem(newItem.get());
- // Ref here because Java expects to adopt the reference, and as such will not
- // call ref on it. However, setBridge has also adopted the reference
- // TODO: This is confusing as hell, clean up ownership and have setBridge
- // take a RefPtr instead of a raw ptr and calling adoptRef on it
- bridge->ref();
- return reinterpret_cast<jint>(bridge);
-}
-
-static void WebHistoryRef(JNIEnv* env, jobject obj, jint ptr)
-{
- if (ptr)
- reinterpret_cast<WebHistoryItem*>(ptr)->ref();
-}
-
-static void WebHistoryUnref(JNIEnv* env, jobject obj, jint ptr)
-{
- if (ptr)
- reinterpret_cast<WebHistoryItem*>(ptr)->deref();
-}
-
-static jobject WebHistoryGetTitle(JNIEnv* env, jobject obj, jint ptr)
-{
- if (!ptr)
- return 0;
- WebHistoryItem* item = reinterpret_cast<WebHistoryItem*>(ptr);
- MutexLocker locker(item->m_lock);
- return wtfStringToJstring(env, item->m_title, false);
-}
-
-static jobject WebHistoryGetUrl(JNIEnv* env, jobject obj, jint ptr)
-{
- if (!ptr)
- return 0;
- WebHistoryItem* item = reinterpret_cast<WebHistoryItem*>(ptr);
- MutexLocker locker(item->m_lock);
- return wtfStringToJstring(env, item->m_url, false);
-}
-
-static jobject WebHistoryGetOriginalUrl(JNIEnv* env, jobject obj, jint ptr)
-{
- if (!ptr)
- return 0;
- WebHistoryItem* item = reinterpret_cast<WebHistoryItem*>(ptr);
- MutexLocker locker(item->m_lock);
- return wtfStringToJstring(env, item->m_originalUrl, false);
-}
-
-static jobject WebHistoryGetFlattenedData(JNIEnv* env, jobject obj, jint ptr)
-{
- if (!ptr)
- return 0;
-
- WebHistoryItem* item = reinterpret_cast<WebHistoryItem*>(ptr);
- MutexLocker locker(item->m_lock);
-
- if (!item->m_dataCached) {
- // Try to create a new java byte array.
- jbyteArray b = env->NewByteArray(item->m_data.size());
- if (!b)
- return NULL;
-
- // Write our flattened data to the java array.
- env->SetByteArrayRegion(b, 0, item->m_data.size(),
- (const jbyte*)item->m_data.data());
- item->m_dataCached = env->NewGlobalRef(b);
- env->DeleteLocalRef(b);
- }
- return item->m_dataCached;
-}
-
-static jobject WebHistoryGetFavicon(JNIEnv* env, jobject obj, jint ptr)
-{
- if (!ptr)
- return 0;
- WebHistoryItem* item = reinterpret_cast<WebHistoryItem*>(ptr);
- MutexLocker locker(item->m_lock);
- if (!item->m_faviconCached && item->m_favicon) {
- jobject favicon = GraphicsJNI::createBitmap(env,
- item->m_favicon,
- false, NULL);
- item->m_favicon = 0; // Framework now owns the pointer
- item->m_faviconCached = env->NewGlobalRef(favicon);
- env->DeleteLocalRef(favicon);
- }
- return item->m_faviconCached;
}
// 6 empty strings + no document state + children count + 2 scales = 10 unsigned values
// 1 char for isTargetItem.
#define HISTORY_MIN_SIZE ((int)(sizeof(unsigned) * 10 + sizeof(char)))
-void WebHistory::Flatten(JNIEnv* env, WTF::Vector<char>& vector, WebCore::HistoryItem* item)
+jbyteArray WebHistory::Flatten(JNIEnv* env, WTF::Vector<char>& v, WebCore::HistoryItem* item)
{
if (!item)
- return;
+ return NULL;
// Reserve a vector of chars with an initial size of HISTORY_MIN_SIZE.
- vector.reserveCapacity(HISTORY_MIN_SIZE);
+ v.reserveCapacity(HISTORY_MIN_SIZE);
// Write the top-level history item and then write all the children
// recursively.
- ALOG_ASSERT(item->bridge(), "Why don't we have a bridge object here?");
- writeItem(vector, item);
- writeChildrenRecursive(vector, item);
+ LOG_ASSERT(item->bridge(), "Why don't we have a bridge object here?");
+ write_item(v, item);
+ write_children_recursive(v, item);
+
+ // Try to create a new java byte array.
+ jbyteArray b = env->NewByteArray(v.size());
+ if (!b)
+ return NULL;
+
+ // Write our flattened data to the java array.
+ env->SetByteArrayRegion(b, 0, v.size(), (const jbyte*)v.data());
+ return b;
+}
+
+WebHistoryItem::WebHistoryItem(JNIEnv* env, jobject obj,
+ WebCore::HistoryItem* item) : WebCore::AndroidWebHistoryBridge(item) {
+ m_object = env->NewWeakGlobalRef(obj);
+ m_parent = 0;
+}
+
+WebHistoryItem::~WebHistoryItem() {
+ if (m_object) {
+ JNIEnv* env = JSC::Bindings::getJNIEnv();
+ if (!env)
+ return;
+ env->DeleteWeakGlobalRef(m_object);
+ }
}
void WebHistoryItem::updateHistoryItem(WebCore::HistoryItem* item) {
@@ -306,7 +246,7 @@ void WebHistoryItem::updateHistoryItem(WebCore::HistoryItem* item) {
// 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().
- ALOGW("Can't updateHistoryItem as the top HistoryItem is gone");
+ LOGW("Can't updateHistoryItem as the top HistoryItem is gone");
return;
}
while (webItem->parent())
@@ -316,7 +256,7 @@ void WebHistoryItem::updateHistoryItem(WebCore::HistoryItem* item) {
// If a HistoryItem only exists for page cache, it is possible that
// the parent HistoryItem destroyed before the child HistoryItem. If
// it happens, skip updating.
- ALOGW("Can't updateHistoryItem as the top HistoryItem is gone");
+ LOGW("Can't updateHistoryItem as the top HistoryItem is gone");
return;
}
}
@@ -324,15 +264,23 @@ void WebHistoryItem::updateHistoryItem(WebCore::HistoryItem* item) {
if (!env)
return;
- MutexLocker locker(webItem->m_lock);
+ // Don't do anything if the item has been gc'd already
+ AutoJObject realItem = getRealObject(env, webItem->m_object);
+ if (!realItem.get())
+ return;
- // TODO: Figure out if we can't just use item->urlString() instead...
const WTF::String urlString = WebFrame::convertIDNToUnicode(item->url());
- webItem->m_url = urlString.threadsafeCopy();
+ jstring urlStr = NULL;
+ if (!urlString.isNull())
+ urlStr = wtfStringToJstring(env, urlString);
const WTF::String originalUrlString = WebFrame::convertIDNToUnicode(item->originalURL());
- webItem->m_originalUrl = originalUrlString.threadsafeCopy();
+ jstring originalUrlStr = NULL;
+ if (!originalUrlString.isNull())
+ originalUrlStr = wtfStringToJstring(env, originalUrlString);
const WTF::String& titleString = item->title();
- webItem->m_title = titleString.threadsafeCopy();
+ jstring titleStr = NULL;
+ if (!titleString.isNull())
+ titleStr = wtfStringToJstring(env, titleString);
// Try to get the favicon from the history item. For some pages like Grand
// Prix, there are history items with anchors. If the icon fails for the
@@ -346,41 +294,24 @@ void WebHistoryItem::updateHistoryItem(WebCore::HistoryItem* item) {
// FIXME: This method should not be used from outside WebCore and will be removed.
// http://trac.webkit.org/changeset/81484
WebCore::Image* icon = WebCore::iconDatabase().synchronousIconForPageURL(url, WebCore::IntSize(16, 16));
- delete webItem->m_favicon;
- webItem->m_favicon = webcoreImageToSkBitmap(icon);
- if (webItem->m_faviconCached) {
- env->DeleteGlobalRef(webItem->m_faviconCached);
- webItem->m_faviconCached = 0;
- }
- webItem->m_data.clear();
- WebHistory::Flatten(env, webItem->m_data, item);
- if (webItem->m_dataCached) {
- env->DeleteGlobalRef(webItem->m_dataCached);
- webItem->m_dataCached = 0;
- }
-}
-
-WebHistoryItem::~WebHistoryItem()
-{
- delete m_favicon;
- JNIEnv* env = JSC::Bindings::getJNIEnv();
- if (!env) {
- ALOGW("Failed to get JNIEnv*! Potential memory leak!");
- return;
- }
- if (m_faviconCached) {
- env->DeleteGlobalRef(m_faviconCached);
- m_faviconCached = 0;
- }
- if (m_dataCached) {
- env->DeleteGlobalRef(m_dataCached);
- m_dataCached = 0;
- }
+ if (icon)
+ favicon = webcoreImageToJavaBitmap(env, icon);
+
+ WTF::Vector<char> data;
+ jbyteArray array = WebHistory::Flatten(env, data, item);
+ env->CallVoidMethod(realItem.get(), gWebHistoryItem.mUpdate, urlStr,
+ originalUrlStr, titleStr, favicon, array);
+ env->DeleteLocalRef(urlStr);
+ env->DeleteLocalRef(originalUrlStr);
+ env->DeleteLocalRef(titleStr);
+ if (favicon)
+ env->DeleteLocalRef(favicon);
+ env->DeleteLocalRef(array);
}
static void historyItemChanged(WebCore::HistoryItem* item) {
- ALOG_ASSERT(item, "historyItemChanged called with a null item");
+ LOG_ASSERT(item, "historyItemChanged called with a null item");
if (item->bridge())
item->bridge()->updateHistoryItem(item);
@@ -388,22 +319,22 @@ static void historyItemChanged(WebCore::HistoryItem* item) {
void WebHistory::AddItem(const AutoJObject& list, WebCore::HistoryItem* item)
{
- ALOG_ASSERT(item, "newItem must take a valid HistoryItem!");
+ 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();
- // Create the bridge, make it active, and attach it to the item.
- WebHistoryItem* bridge = new WebHistoryItem(item);
- bridge->setActive();
- item->setBridge(bridge);
// Allocate a blank WebHistoryItem
jclass clazz = env->FindClass("android/webkit/WebHistoryItem");
- jobject newItem = env->NewObject(clazz, gWebHistoryItem.mInit,
- reinterpret_cast<int>(bridge));
+ jobject newItem = env->NewObject(clazz, gWebHistoryItem.mInit);
env->DeleteLocalRef(clazz);
+ // Create the bridge, make it active, and attach it to the item.
+ WebHistoryItem* bridge = new WebHistoryItem(env, newItem, item);
+ bridge->setActive();
+ item->setBridge(bridge);
+
// Update the history item which will flatten the data and call update on
// the java item.
bridge->updateHistoryItem(item);
@@ -427,7 +358,7 @@ void WebHistory::UpdateHistoryIndex(const AutoJObject& list, int newIndex)
list.env()->CallVoidMethod(list.get(), gWebBackForwardList.mSetCurrentIndex, newIndex);
}
-static void writeString(WTF::Vector<char>& vector, const WTF::String& str)
+static void write_string(WTF::Vector<char>& v, const WTF::String& str)
{
unsigned strLen = str.length();
// Only do work if the string has data.
@@ -435,96 +366,96 @@ static void writeString(WTF::Vector<char>& vector, const WTF::String& str)
// 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 = vector.size() + sizeof(unsigned);
+ 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.
- vector.grow(length);
+ v.grow(length);
// Grab the position to write to.
- char* data = vector.begin() + vectorLen;
+ char* data = v.begin() + vectorLen;
// Write the actual string
int l = SkUTF16_ToUTF8(str.characters(), strLen, data);
- ALOGV("Writing string %d %.*s", l, l, 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.
- vector.shrink(vectorLen + l);
+ v.shrink(vectorLen + l);
} else
- vector.append((char*)&strLen, sizeof(unsigned));
+ v.append((char*)&strLen, sizeof(unsigned));
}
-static void writeItem(WTF::Vector<char>& vector, WebCore::HistoryItem* item)
+static void write_item(WTF::Vector<char>& v, WebCore::HistoryItem* item)
{
// Original url
- writeString(vector, item->originalURLString());
+ write_string(v, item->originalURLString());
// Url
- writeString(vector, item->urlString());
+ write_string(v, item->urlString());
// Title
- writeString(vector, item->title());
+ write_string(v, item->title());
// Form content type
- writeString(vector, item->formContentType());
+ write_string(v, item->formContentType());
// Form data
const WebCore::FormData* formData = item->formData();
if (formData) {
- writeString(vector, formData->flattenToString());
+ write_string(v, formData->flattenToString());
// save the identifier as it is not included in the flatten data
int64_t id = formData->identifier();
- vector.append((char*)&id, sizeof(int64_t));
+ v.append((char*)&id, sizeof(int64_t));
} else
- writeString(vector, WTF::String()); // Empty constructor does not allocate a buffer.
+ write_string(v, WTF::String()); // Empty constructor does not allocate a buffer.
// Target
- writeString(vector, item->target());
+ write_string(v, item->target());
AndroidWebHistoryBridge* bridge = item->bridge();
- ALOG_ASSERT(bridge, "We should have a bridge here!");
+ LOG_ASSERT(bridge, "We should have a bridge here!");
// Screen scale
const float scale = bridge->scale();
- ALOGV("Writing scale %f", scale);
- vector.append((char*)&scale, sizeof(float));
+ LOGV("Writing scale %f", scale);
+ v.append((char*)&scale, sizeof(float));
const float textWrapScale = bridge->textWrapScale();
- ALOGV("Writing text wrap scale %f", textWrapScale);
- vector.append((char*)&textWrapScale, sizeof(float));
+ LOGV("Writing text wrap scale %f", textWrapScale);
+ v.append((char*)&textWrapScale, sizeof(float));
// Scroll position.
const int scrollX = item->scrollPoint().x();
- vector.append((char*)&scrollX, sizeof(int));
+ v.append((char*)&scrollX, sizeof(int));
const int scrollY = item->scrollPoint().y();
- vector.append((char*)&scrollY, sizeof(int));
+ v.append((char*)&scrollY, sizeof(int));
// Document state
const WTF::Vector<WTF::String>& docState = item->documentState();
WTF::Vector<WTF::String>::const_iterator end = docState.end();
unsigned stateSize = docState.size();
- ALOGV("Writing docState %d", stateSize);
- vector.append((char*)&stateSize, sizeof(unsigned));
+ LOGV("Writing docState %d", stateSize);
+ v.append((char*)&stateSize, sizeof(unsigned));
for (WTF::Vector<WTF::String>::const_iterator i = docState.begin(); i != end; ++i) {
- writeString(vector, *i);
+ write_string(v, *i);
}
// Is target item
- ALOGV("Writing isTargetItem %d", item->isTargetItem());
- vector.append((char)item->isTargetItem());
+ LOGV("Writing isTargetItem %d", item->isTargetItem());
+ v.append((char)item->isTargetItem());
// Children count
unsigned childCount = item->children().size();
- ALOGV("Writing childCount %d", childCount);
- vector.append((char*)&childCount, sizeof(unsigned));
+ LOGV("Writing childCount %d", childCount);
+ v.append((char*)&childCount, sizeof(unsigned));
}
-static void writeChildrenRecursive(WTF::Vector<char>& vector, WebCore::HistoryItem* parent)
+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();
- ALOG_ASSERT(parent->bridge(),
+ LOG_ASSERT(parent->bridge(),
"The parent item should have a bridge object!");
if (!item->bridge()) {
WebHistoryItem* bridge = new WebHistoryItem(static_cast<WebHistoryItem*>(parent->bridge()));
@@ -536,223 +467,116 @@ static void writeChildrenRecursive(WTF::Vector<char>& vector, WebCore::HistoryIt
// parent must not have a parent bridge.
WebHistoryItem* bridge = static_cast<WebHistoryItem*>(item->bridge());
WebHistoryItem* parentBridge = static_cast<WebHistoryItem*>(parent->bridge());
- ALOG_ASSERT(parentBridge->parent() == 0 ||
+ LOG_ASSERT(parentBridge->parent() == 0 ||
bridge->parent() == parentBridge,
"Somehow this item has an incorrect parent");
bridge->setParent(parentBridge);
}
- writeItem(vector, item);
- writeChildrenRecursive(vector, item);
- }
-}
-
-bool readUnsigned(const char*& data, const char* end, unsigned& result, const char* dbgLabel = 0);
-bool readInt(const char*& data, const char* end, int& result, const char* dbgLabel = 0);
-bool readInt64(const char*& data, const char* end, int64_t& result, const char* dbgLabel = 0);
-bool readFloat(const char*& data, const char* end, float& result, const char* dbgLabel = 0);
-bool readBool(const char*& data, const char* end, bool& result, const char* dbgLabel = 0);
-bool readString(const char*& data, const char* end, String& result, const char* dbgLabel = 0);
-
-bool readUnsigned(const char*& data, const char* end, unsigned& result, const char* dbgLabel)
-{
- // Check if we have enough data left to continue.
- if ((end < data) || (static_cast<size_t>(end - data) < sizeof(unsigned))) {
- ALOGW("\tNot enough data to read unsigned; tag=\"%s\" end=%p data=%p",
- dbgLabel ? dbgLabel : "<no tag>", end, data);
- return false;
- }
-
- memcpy(&result, data, sizeof(unsigned));
- data += sizeof(unsigned);
- if (dbgLabel)
- ALOGV("Reading %-16s %u", dbgLabel, result);
- return true;
-}
-
-bool readInt(const char*& data, const char* end, int& result, const char* dbgLabel)
-{
- // Check if we have enough data left to continue.
- if ((end < data) || (static_cast<size_t>(end - data) < sizeof(int))) {
- ALOGW("Not enough data to read int; tag=\"%s\" end=%p data=%p",
- dbgLabel ? dbgLabel : "<no tag>", end, data);
- return false;
+ write_item(v, item);
+ write_children_recursive(v, item);
}
-
- memcpy(&result, data, sizeof(int));
- data += sizeof(int);
- if (dbgLabel)
- ALOGV("Reading %-16s %d", dbgLabel, result);
- return true;
}
-bool readInt64(const char*& data, const char* end, int64_t& result, const char* dbgLabel)
-{
- // Check if we have enough data left to continue.
- if ((end < data) || (static_cast<size_t>(end - data) < sizeof(int64_t))) {
- ALOGW("Not enough data to read int64_t; tag=\"%s\" end=%p data=%p",
- dbgLabel ? dbgLabel : "<no tag>", end, data);
- return false;
- }
-
- memcpy(&result, data, sizeof(int64_t));
- data += sizeof(int64_t);
- if (dbgLabel)
- ALOGV("Reading %-16s %ll", dbgLabel, result);
- return true;
-}
-
-bool readFloat(const char*& data, const char* end, float& result, const char* dbgLabel)
-{
- // Check if we have enough data left to continue.
- if ((end < data) || (static_cast<size_t>(end - data) < sizeof(float))) {
- ALOGW("Not enough data to read float; tag=\"%s\" end=%p data=%p",
- dbgLabel ? dbgLabel : "<no tag>", end, data);
- return false;
- }
-
- memcpy(&result, data, sizeof(float));
- data += sizeof(float);
- if (dbgLabel)
- ALOGV("Reading %-16s %f", dbgLabel, result);
- return true;
-}
-
-// Note that the return value indicates success or failure, while the result
-// parameter indicates the read value of the bool
-bool readBool(const char*& data, const char* end, bool& result, const char* dbgLabel)
-{
- // Check if we have enough data left to continue.
- if ((end < data) || (static_cast<size_t>(end - data) < sizeof(char))) {
- ALOGW("Not enough data to read bool; tag=\"%s\" end=%p data=%p",
- dbgLabel ? dbgLabel : "<no tag>", end, data);
- return false;
- }
-
- char c;
- memcpy(&c, data, sizeof(char));
- data += sizeof(char);
- if (dbgLabel)
- ALOGV("Reading %-16s %d", dbgLabel, c);
- result = c;
-
- // Valid bool results are 0 or 1
- if ((c != 0) && (c != 1)) {
- ALOGW("Invalid value for bool; tag=\"%s\" end=%p data=%p c=%u",
- dbgLabel ? dbgLabel : "<no tag>", end, data, c);
- return false;
- }
-
- return true;
-}
-
-bool readString(const char*& data, const char* end, String& result, const char* dbgLabel)
-{
- unsigned stringLength;
- if (!readUnsigned(data, end, stringLength)) {
- ALOGW("Not enough data to read string length; tag=\"%s\" end=%p data=%p",
- dbgLabel ? dbgLabel : "<no tag>", end, data);
- return false;
- }
-
- if (dbgLabel)
- ALOGV("Reading %-16s %d %.*s", dbgLabel, stringLength, stringLength, data);
-
- // If length was 0, there will be no string content, but still return true
- if (!stringLength) {
- result = String();
- return true;
- }
-
- if ((end < data) || ((unsigned)(end - data) < stringLength)) {
- ALOGW("Not enough data to read content; tag=\"%s\" end=%p data=%p stringLength=%u",
- dbgLabel ? dbgLabel : "<no tag>", end, data, stringLength);
- return false;
- }
-
- const unsigned MAX_REASONABLE_STRING_LENGTH = 10000;
- if (stringLength > MAX_REASONABLE_STRING_LENGTH) {
- ALOGW("String length is suspiciously large (>%d); tag=\"%s\" end=%p data=%p stringLength=%u",
- MAX_REASONABLE_STRING_LENGTH, dbgLabel ? dbgLabel : "<no tag>",
- end, data, stringLength);
- }
-
- bool decodeFailed = false;
- static const WebCore::TextEncoding& encoding = WebCore::UTF8Encoding();
- result = encoding.decode(data, stringLength, true, decodeFailed);
- if (decodeFailed) {
- ALOGW("Decode failed, tag=\"%s\" end=%p data=%p stringLength=%u content=\"%s\"",
- dbgLabel ? dbgLabel : "<no tag>", end, data, stringLength,
- result.utf8().data());
- return false;
- }
-
- if (stringLength > MAX_REASONABLE_STRING_LENGTH) {
- ALOGW("\tdecodeFailed=%d (flag is ignored) content=\"%s\"",
- decodeFailed, result.utf8().data());
- }
-
- data += stringLength;
- return true;
-}
-
-static bool readItemRecursive(WebCore::HistoryItem* newItem,
+static bool read_item_recursive(WebCore::HistoryItem* newItem,
const char** pData, int length)
{
- if (!pData || length < HISTORY_MIN_SIZE) {
- ALOGW("readItemRecursive() bad params; pData=%p length=%d", pData, length);
+ if (!pData || length < HISTORY_MIN_SIZE)
return false;
- }
+ const WebCore::TextEncoding& e = WebCore::UTF8Encoding();
const char* data = *pData;
const char* end = data + length;
- String content;
+ int sizeofUnsigned = (int)sizeof(unsigned);
// Read the original url
- if (readString(data, end, content, "Original url"))
- newItem->setOriginalURLString(content);
- else
+ // Read the expected length of the string.
+ unsigned 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
- if (readString(data, end, content, "Url"))
- newItem->setURLString(content);
- else
+ 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
- if (readString(data, end, content, "Title"))
- newItem->setTitle(content);
- else
+ 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.
- // Read the form content type
WTF::String formContentType;
- if (!readString(data, end, formContentType, "Content type"))
- return false;
+ WTF::PassRefPtr<WebCore::FormData> formData = NULL;
- // Read the form data size
- unsigned formDataSize;
- if (!readUnsigned(data, end, formDataSize, "Form data size"))
+ // Read the form content type
+ memcpy(&l, data, sizeofUnsigned);
+ data += sizeofUnsigned;
+ if (l) {
+ LOGV("Content type %d %.*s", l, l, data);
+ if (data + l < end)
+ formContentType = e.decode(data, l);
+ else
+ return false;
+ data += l;
+ }
+ if (end - data < sizeofUnsigned)
return false;
// Read the form data
- WTF::RefPtr<WebCore::FormData> formData;
- if (formDataSize) {
- ALOGV("Reading Form data %d %.*s", formDataSize, formDataSize, data);
- if ((end < data) || ((size_t)(end - data) < formDataSize)) {
- ALOGW("\tNot enough data to read form data; returning");
+ 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;
- }
- formData = WebCore::FormData::create(data, formDataSize);
- data += formDataSize;
+ data += l;
// Read the identifier
- int64_t id;
- if (!readInt64(data, end, id, "Form id"))
- return false;
- if (id)
- formData->setIdentifier(id);
+ {
+ int64_t id;
+ int size = (int)sizeof(int64_t);
+ memcpy(&id, data, size);
+ data += size;
+ if (id)
+ formData->setIdentifier(id);
+ }
}
+ if (end - data < sizeofUnsigned)
+ return false;
// Set up the form info
if (formData != NULL) {
@@ -764,76 +588,112 @@ static bool readItemRecursive(WebCore::HistoryItem* newItem,
}
// Read the target
- if (readString(data, end, content, "Target"))
- newItem->setTarget(content);
- else
+ memcpy(&l, data, sizeofUnsigned);
+ data += sizeofUnsigned;
+ if (l) {
+ LOGV("Target %d %.*s", l, l, data);
+ if (data + l < end)
+ newItem->setTarget(e.decode(data, l));
+ else
+ return false;
+ data += l;
+ }
+ if (end - data < sizeofUnsigned)
return false;
AndroidWebHistoryBridge* bridge = newItem->bridge();
- ALOG_ASSERT(bridge, "There should be a bridge object during inflate");
-
- // Read the screen scale
+ LOG_ASSERT(bridge, "There should be a bridge object during inflate");
float fValue;
- if (readFloat(data, end, fValue, "Screen scale"))
- bridge->setScale(fValue);
- else
- return false;
+ // Read the screen scale
+ memcpy(&fValue, data, sizeof(float));
+ LOGV("Screen scale %f", fValue);
+ bridge->setScale(fValue);
+ data += sizeof(float);
+ memcpy(&fValue, data, sizeofUnsigned);
+ LOGV("Text wrap scale %f", fValue);
+ bridge->setTextWrapScale(fValue);
+ data += sizeof(float);
- // Read the text wrap scale
- if (readFloat(data, end, fValue, "Text wrap scale"))
- bridge->setTextWrapScale(fValue);
- else
+ if (end - data < sizeofUnsigned)
return false;
// Read scroll position.
- int scrollX;
- if (!readInt(data, end, scrollX, "Scroll pos x"))
- return false;
- int scrollY;
- if (!readInt(data, end, scrollY, "Scroll pos y"))
- return false;
+ int scrollX = 0;
+ memcpy(&scrollX, data, sizeofUnsigned);
+ data += sizeofUnsigned;
+ int scrollY = 0;
+ memcpy(&scrollY, data, sizeofUnsigned);
+ data += sizeofUnsigned;
newItem->setScrollPoint(IntPoint(scrollX, scrollY));
- // Read the document state
- unsigned docStateCount;
- if (!readUnsigned(data, end, docStateCount, "Doc state count"))
+ if (end - data < sizeofUnsigned)
return false;
- if (docStateCount) {
+
+ // Read the document state
+ memcpy(&l, data, sizeofUnsigned);
+ LOGV("Document state %d", l);
+ data += sizeofUnsigned;
+ if (l) {
+ // Check if we have enough data to at least parse the sizes of each
+ // document state string.
+ if (data + l * sizeofUnsigned >= end)
+ return false;
// Create a new vector and reserve enough space for the document state.
WTF::Vector<WTF::String> docState;
- docState.reserveCapacity(docStateCount);
- while (docStateCount--) {
- // Read a document state string
- if (readString(data, end, content, "Document state"))
- docState.append(content);
+ 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
- bool c;
- if (readBool(data, end, c, "Target item"))
- newItem->setIsTargetItem(c);
- else
+ // 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
- unsigned count;
- if (!readUnsigned(data, end, count, "Child count"))
- return false;
+ memcpy(&l, data, sizeofUnsigned);
+ LOGV("Child count %d", l);
+ data += sizeofUnsigned;
*pData = data;
- if (count) {
- while (count--) {
+ 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::RefPtr<WebCore::HistoryItem> child = WebCore::HistoryItem::create();
+ WTF::PassRefPtr<WebCore::HistoryItem> child = WebCore::HistoryItem::create();
// Set a bridge that will not call into java.
child->setBridge(new WebHistoryItem(static_cast<WebHistoryItem*>(bridge)));
// Read the child item.
- if (!readItemRecursive(child.get(), pData, end - data))
+ if (!read_item_recursive(child.get(), pData, end - data)) {
+ child.clear();
return false;
+ }
child->bridge()->setActive();
newItem->addChildItem(child);
}
@@ -849,86 +709,83 @@ static bool readItemRecursive(WebCore::HistoryItem* newItem,
// 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 unitTest()
+static void unit_test()
{
- ALOGD("Entering history unit test!");
+ LOGD("Entering history unit test!");
const char* test1 = new char[0];
WTF::RefPtr<WebCore::HistoryItem> item = WebCore::HistoryItem::create();
WebCore::HistoryItem* testItem = item.get();
testItem->setBridge(new WebHistoryItem(0));
- ALOG_ASSERT(!readItemRecursive(testItem, &test1, 0), "0 length array should fail!");
+ LOG_ASSERT(!read_item_recursive(testItem, &test1, 0), "0 length array should fail!");
delete[] test1;
const char* test2 = new char[2];
- ALOG_ASSERT(!readItemRecursive(testItem, &test2, 2), "Small array should fail!");
+ LOG_ASSERT(!read_item_recursive(testItem, &test2, 2), "Small array should fail!");
delete[] test2;
- ALOG_ASSERT(!readItemRecursive(testItem, NULL, HISTORY_MIN_SIZE), "Null data should fail!");
+ 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;
- ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length originalUrl should fail!");
+ 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;
- ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length url should fail!");
+ 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;
- ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length title should fail!");
+ 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;
- ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length contentType should fail!");
+ LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length contentType should fail!");
// Form data
offset += 4;
memset(test3, 0, HISTORY_MIN_SIZE);
ptr = (const char*)test3;
*(int*)(test3 + offset) = 4000;
- ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length form data should fail!");
+ 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;
- ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length target should fail!");
- offset += 4; // Screen scale
- offset += 4; // Text wrap scale
- offset += 4; // Scroll pos x
- offset += 4; // Scroll pos y
+ LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length target should fail!");
+ offset += 4; // Scale
// Document state
offset += 4;
memset(test3, 0, HISTORY_MIN_SIZE);
ptr = (const char*)test3;
*(int*)(test3 + offset) = 4000;
- ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length document state should fail!");
+ 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) = '!';
- ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "IsTargetItem should fail with ! as the value!");
+ 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;
- ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 kids should fail!");
+ LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 kids should fail!");
+ offset = 36;
// Test document state
- offset = 40;
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;
- ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE + sizeof(unsigned)), "1 20 length document state string should fail!");
+ 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));
@@ -936,9 +793,8 @@ static void unitTest()
*(int*)(test3 + offset) = 2;
*(int*)(test3 + offset + 4) = 0;
*(int*)(test3 + offset + 8) = 20;
- ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE + 2 * sizeof(unsigned) ), "2 20 length document state string should fail!");
+ LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE + 2 * sizeof(unsigned) ), "2 20 length document state string should fail!");
delete[] test3;
- ALOGD("Leaving history unit test!");
}
#endif
@@ -953,22 +809,8 @@ static JNINativeMethod gWebBackForwardListMethods[] = {
};
static JNINativeMethod gWebHistoryItemMethods[] = {
- { "inflate", "(I[B)I",
- (void*) WebHistoryInflate },
- { "nativeRef", "(I)V",
- (void*) WebHistoryRef },
- { "nativeUnref", "(I)V",
- (void*) WebHistoryUnref },
- { "nativeGetTitle", "(I)Ljava/lang/String;",
- (void*) WebHistoryGetTitle },
- { "nativeGetUrl", "(I)Ljava/lang/String;",
- (void*) WebHistoryGetUrl },
- { "nativeGetOriginalUrl", "(I)Ljava/lang/String;",
- (void*) WebHistoryGetOriginalUrl },
- { "nativeGetFlattenedData", "(I)[B",
- (void*) WebHistoryGetFlattenedData },
- { "nativeGetFavicon", "(I)Landroid/graphics/Bitmap;",
- (void*) WebHistoryGetFavicon },
+ { "inflate", "(I[B)V",
+ (void*) WebHistoryInflate }
};
int registerWebHistory(JNIEnv* env)
@@ -976,27 +818,35 @@ int registerWebHistory(JNIEnv* env)
// Get notified of all changes to history items.
WebCore::notifyHistoryItemChanged = historyItemChanged;
#ifdef UNIT_TEST
- unitTest();
+ unit_test();
#endif
// Find WebHistoryItem, its constructor, and the update method.
jclass clazz = env->FindClass("android/webkit/WebHistoryItem");
- ALOG_ASSERT(clazz, "Unable to find class android/webkit/WebHistoryItem");
- gWebHistoryItem.mInit = env->GetMethodID(clazz, "<init>", "(I)V");
- ALOG_ASSERT(gWebHistoryItem.mInit, "Could not find WebHistoryItem constructor");
-
+ LOG_ASSERT(clazz, "Unable to find class android/webkit/WebHistoryItem");
+ gWebHistoryItem.mInit = env->GetMethodID(clazz, "<init>", "()V");
+ LOG_ASSERT(gWebHistoryItem.mInit, "Could not find WebHistoryItem constructor");
+ gWebHistoryItem.mUpdate = env->GetMethodID(clazz, "update",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/graphics/Bitmap;[B)V");
+ LOG_ASSERT(gWebHistoryItem.mUpdate, "Could not find method update in WebHistoryItem");
+
+ // Find the field ids for mTitle and mUrl.
+ gWebHistoryItem.mTitle = env->GetFieldID(clazz, "mTitle", "Ljava/lang/String;");
+ LOG_ASSERT(gWebHistoryItem.mTitle, "Could not find field mTitle in WebHistoryItem");
+ gWebHistoryItem.mUrl = env->GetFieldID(clazz, "mUrl", "Ljava/lang/String;");
+ LOG_ASSERT(gWebHistoryItem.mUrl, "Could not find field mUrl in WebHistoryItem");
env->DeleteLocalRef(clazz);
// Find the WebBackForwardList object and method.
clazz = env->FindClass("android/webkit/WebBackForwardList");
- ALOG_ASSERT(clazz, "Unable to find class android/webkit/WebBackForwardList");
+ LOG_ASSERT(clazz, "Unable to find class android/webkit/WebBackForwardList");
gWebBackForwardList.mAddHistoryItem = env->GetMethodID(clazz, "addHistoryItem",
"(Landroid/webkit/WebHistoryItem;)V");
- ALOG_ASSERT(gWebBackForwardList.mAddHistoryItem, "Could not find method addHistoryItem");
+ LOG_ASSERT(gWebBackForwardList.mAddHistoryItem, "Could not find method addHistoryItem");
gWebBackForwardList.mRemoveHistoryItem = env->GetMethodID(clazz, "removeHistoryItem",
"(I)V");
- ALOG_ASSERT(gWebBackForwardList.mRemoveHistoryItem, "Could not find method removeHistoryItem");
+ LOG_ASSERT(gWebBackForwardList.mRemoveHistoryItem, "Could not find method removeHistoryItem");
gWebBackForwardList.mSetCurrentIndex = env->GetMethodID(clazz, "setCurrentIndex", "(I)V");
- ALOG_ASSERT(gWebBackForwardList.mSetCurrentIndex, "Could not find method setCurrentIndex");
+ LOG_ASSERT(gWebBackForwardList.mSetCurrentIndex, "Could not find method setCurrentIndex");
env->DeleteLocalRef(clazz);
int result = jniRegisterNativeMethods(env, "android/webkit/WebBackForwardList",