diff options
Diffstat (limited to 'Source/WebKit/android/jni/WebHistory.cpp')
-rw-r--r-- | Source/WebKit/android/jni/WebHistory.cpp | 754 |
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", |