Home | History | Annotate | Download | only in jni
      1 /*
      2  * Copyright 2007, The Android Open Source Project
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  *  * Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  *  * Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #define LOG_TAG "webhistory"
     27 
     28 #include "config.h"
     29 #include "WebHistory.h"
     30 
     31 #include "AndroidLog.h"
     32 #include "BackForwardList.h"
     33 #include "BackForwardListImpl.h"
     34 #include "DocumentLoader.h"
     35 #include "Frame.h"
     36 #include "FrameLoader.h"
     37 #include "FrameLoaderClientAndroid.h"
     38 #include "FrameTree.h"
     39 #include "GraphicsJNI.h"
     40 #include "HistoryItem.h"
     41 #include "IconDatabase.h"
     42 #include "Page.h"
     43 #include "TextEncoding.h"
     44 #include "WebCoreFrameBridge.h"
     45 #include "WebCoreJni.h"
     46 #include "WebIconDatabase.h"
     47 
     48 #include <JNIHelp.h>
     49 #include "JNIUtility.h"
     50 #include <SkUtils.h>
     51 #include <utils/misc.h>
     52 #include <wtf/OwnPtr.h>
     53 #include <wtf/Platform.h>
     54 #include <wtf/text/CString.h>
     55 
     56 namespace android {
     57 
     58 // Forward declarations
     59 static void writeItem(WTF::Vector<char>& vector, WebCore::HistoryItem* item);
     60 static void writeChildrenRecursive(WTF::Vector<char>& vector, WebCore::HistoryItem* parent);
     61 static bool readItemRecursive(WebCore::HistoryItem* child, const char** pData, int length);
     62 
     63 // Field ids for WebHistoryClassicItems
     64 struct WebHistoryItemClassicFields {
     65     jmethodID   mInit;
     66 } gWebHistoryItemClassic;
     67 
     68 struct WebBackForwardListClassicFields {
     69     jmethodID   mAddHistoryItem;
     70     jmethodID   mRemoveHistoryItem;
     71     jmethodID   mSetCurrentIndex;
     72 } gWebBackForwardListClassic;
     73 
     74 //--------------------------------------------------------------------------
     75 // WebBackForwardListClassic native methods.
     76 //--------------------------------------------------------------------------
     77 
     78 static void WebHistoryClose(JNIEnv* env, jobject obj, jint frame)
     79 {
     80     ALOG_ASSERT(frame, "Close needs a valid Frame pointer!");
     81     WebCore::Frame* pFrame = (WebCore::Frame*)frame;
     82 
     83     WebCore::BackForwardListImpl* list = static_cast<WebCore::BackForwardListImpl*>(pFrame->page()->backForwardList());
     84     RefPtr<WebCore::HistoryItem> current = list->currentItem();
     85     // Remove each item instead of using close(). close() is intended to be used
     86     // right before the list is deleted.
     87     WebCore::HistoryItemVector& entries = list->entries();
     88     int size = entries.size();
     89     for (int i = size - 1; i >= 0; --i)
     90         list->removeItem(entries[i].get());
     91     // Add the current item back to the list.
     92     if (current) {
     93         current->setBridge(0);
     94         // addItem will update the children to match the newly created bridge
     95         list->addItem(current);
     96 
     97         /*
     98          * The Grand Prix site uses anchor navigations to change the display.
     99          * WebKit tries to be smart and not load child frames that have the
    100          * same history urls during an anchor navigation. This means that the
    101          * current history item stored in the child frame's loader does not
    102          * match the item found in the history tree. If we remove all the
    103          * entries in the back/foward list, we have to restore the entire tree
    104          * or else a HistoryItem might have a deleted parent.
    105          *
    106          * In order to restore the history tree correctly, we have to look up
    107          * all the frames first and then look up the history item. We do this
    108          * because the history item in the tree may be null at this point.
    109          * Unfortunately, a HistoryItem can only search its immediately
    110          * children so we do a breadth-first rebuild of the tree.
    111          */
    112 
    113         // Keep a small list of child frames to traverse.
    114         WTF::Vector<WebCore::Frame*> frameQueue;
    115         // Fix the top-level item.
    116         pFrame->loader()->history()->setCurrentItem(current.get());
    117         WebCore::Frame* child = pFrame->tree()->firstChild();
    118         // Remember the parent history item so we can search for a child item.
    119         RefPtr<WebCore::HistoryItem> parent = current;
    120         while (child) {
    121             // Use the old history item since the current one may have a
    122             // deleted parent.
    123             WebCore::HistoryItem* item = parent->childItemWithTarget(child->tree()->name());
    124             child->loader()->history()->setCurrentItem(item);
    125             // Append the first child to the queue if it exists. If there is no
    126             // item, then we do not need to traverse the children since there
    127             // will be no parent history item.
    128             WebCore::Frame* firstChild;
    129             if (item && (firstChild = child->tree()->firstChild()))
    130                 frameQueue.append(firstChild);
    131             child = child->tree()->nextSibling();
    132             // If we don't have a sibling for this frame and the queue isn't
    133             // empty, use the next entry in the queue.
    134             if (!child && !frameQueue.isEmpty()) {
    135                 child = frameQueue.at(0);
    136                 frameQueue.remove(0);
    137                 // Figure out the parent history item used when searching for
    138                 // the history item to use.
    139                 parent = child->tree()->parent()->loader()->history()->currentItem();
    140             }
    141         }
    142     }
    143 }
    144 
    145 static void WebHistoryRestoreIndex(JNIEnv* env, jobject obj, jint frame, jint index)
    146 {
    147     ALOG_ASSERT(frame, "RestoreState needs a valid Frame pointer!");
    148     WebCore::Frame* pFrame = (WebCore::Frame*)frame;
    149     WebCore::Page* page = pFrame->page();
    150     WebCore::HistoryItem* currentItem =
    151             static_cast<WebCore::BackForwardListImpl*>(page->backForwardList())->entries()[index].get();
    152 
    153     // load the current page with FrameLoadTypeIndexedBackForward so that it
    154     // will use cache when it is possible
    155     page->goToItem(currentItem, FrameLoadTypeIndexedBackForward);
    156 }
    157 
    158 static jint WebHistoryInflate(JNIEnv* env, jobject obj, jint frame, jbyteArray data)
    159 {
    160     ALOG_ASSERT(frame, "Inflate needs a valid frame pointer!");
    161     ALOG_ASSERT(data, "Inflate needs a valid data pointer!");
    162 
    163     // Get the actual bytes and the length from the java array.
    164     const jbyte* bytes = env->GetByteArrayElements(data, NULL);
    165     jsize size = env->GetArrayLength(data);
    166 
    167     // Inflate the history tree into one HistoryItem or null if the inflation
    168     // failed.
    169     RefPtr<WebCore::HistoryItem> newItem = WebCore::HistoryItem::create();
    170     WebHistoryItem* bridge = new WebHistoryItem(newItem.get());
    171     newItem->setBridge(bridge);
    172 
    173     // Inflate the item recursively. If it fails, that is ok. We'll have an
    174     // incomplete HistoryItem but that is better than crashing due to a null
    175     // item.
    176     // We have a 2nd local variable since read_item_recursive may change the
    177     // ptr's value. We can't pass &bytes since we have to send bytes to
    178     // ReleaseByteArrayElements unchanged.
    179     const char* ptr = reinterpret_cast<const char*>(bytes);
    180     readItemRecursive(newItem.get(), &ptr, (int)size);
    181     env->ReleaseByteArrayElements(data, const_cast<jbyte*>(bytes), JNI_ABORT);
    182     bridge->setActive();
    183 
    184     // Add the new item to the back/forward list.
    185     WebCore::Frame* pFrame = (WebCore::Frame*)frame;
    186     pFrame->page()->backForwardList()->addItem(newItem);
    187 
    188     // Update the item.
    189     bridge->updateHistoryItem(newItem.get());
    190     // Ref here because Java expects to adopt the reference, and as such will not
    191     // call ref on it. However, setBridge has also adopted the reference
    192     // TODO: This is confusing as hell, clean up ownership and have setBridge
    193     // take a RefPtr instead of a raw ptr and calling adoptRef on it
    194     bridge->ref();
    195     return reinterpret_cast<jint>(bridge);
    196 }
    197 
    198 static void WebHistoryRef(JNIEnv* env, jobject obj, jint ptr)
    199 {
    200     if (ptr)
    201         reinterpret_cast<WebHistoryItem*>(ptr)->ref();
    202 }
    203 
    204 static void WebHistoryUnref(JNIEnv* env, jobject obj, jint ptr)
    205 {
    206     if (ptr)
    207         reinterpret_cast<WebHistoryItem*>(ptr)->deref();
    208 }
    209 
    210 static jobject WebHistoryGetTitle(JNIEnv* env, jobject obj, jint ptr)
    211 {
    212     if (!ptr)
    213         return 0;
    214     WebHistoryItem* item = reinterpret_cast<WebHistoryItem*>(ptr);
    215     MutexLocker locker(item->m_lock);
    216     return wtfStringToJstring(env, item->m_title, false);
    217 }
    218 
    219 static jobject WebHistoryGetUrl(JNIEnv* env, jobject obj, jint ptr)
    220 {
    221     if (!ptr)
    222         return 0;
    223     WebHistoryItem* item = reinterpret_cast<WebHistoryItem*>(ptr);
    224     MutexLocker locker(item->m_lock);
    225     return wtfStringToJstring(env, item->m_url, false);
    226 }
    227 
    228 static jobject WebHistoryGetOriginalUrl(JNIEnv* env, jobject obj, jint ptr)
    229 {
    230     if (!ptr)
    231         return 0;
    232     WebHistoryItem* item = reinterpret_cast<WebHistoryItem*>(ptr);
    233     MutexLocker locker(item->m_lock);
    234     return wtfStringToJstring(env, item->m_originalUrl, false);
    235 }
    236 
    237 static jobject WebHistoryGetFlattenedData(JNIEnv* env, jobject obj, jint ptr)
    238 {
    239     if (!ptr)
    240         return 0;
    241 
    242     WebHistoryItem* item = reinterpret_cast<WebHistoryItem*>(ptr);
    243     MutexLocker locker(item->m_lock);
    244 
    245     if (!item->m_dataCached) {
    246         // Try to create a new java byte array.
    247         jbyteArray b = env->NewByteArray(item->m_data.size());
    248         if (!b)
    249             return NULL;
    250 
    251         // Write our flattened data to the java array.
    252         env->SetByteArrayRegion(b, 0, item->m_data.size(),
    253                                 (const jbyte*)item->m_data.data());
    254         item->m_dataCached = env->NewGlobalRef(b);
    255         env->DeleteLocalRef(b);
    256     }
    257     return item->m_dataCached;
    258 }
    259 
    260 static jobject WebHistoryGetFavicon(JNIEnv* env, jobject obj, jint ptr)
    261 {
    262     if (!ptr)
    263         return 0;
    264     WebHistoryItem* item = reinterpret_cast<WebHistoryItem*>(ptr);
    265     MutexLocker locker(item->m_lock);
    266     if (!item->m_faviconCached && item->m_favicon) {
    267         jobject favicon = GraphicsJNI::createBitmap(env,
    268                                                     item->m_favicon,
    269                                                     false, NULL);
    270         item->m_favicon = 0; // Framework now owns the pointer
    271         item->m_faviconCached = env->NewGlobalRef(favicon);
    272         env->DeleteLocalRef(favicon);
    273     }
    274     return item->m_faviconCached;
    275 }
    276 
    277 // 6 empty strings + no document state + children count + 2 scales = 10 unsigned values
    278 // 1 char for isTargetItem.
    279 #define HISTORY_MIN_SIZE ((int)(sizeof(unsigned) * 10 + sizeof(char)))
    280 
    281 void WebHistory::Flatten(JNIEnv* env, WTF::Vector<char>& vector, WebCore::HistoryItem* item)
    282 {
    283     if (!item)
    284         return;
    285 
    286     // Reserve a vector of chars with an initial size of HISTORY_MIN_SIZE.
    287     vector.reserveCapacity(HISTORY_MIN_SIZE);
    288 
    289     // Write the top-level history item and then write all the children
    290     // recursively.
    291     ALOG_ASSERT(item->bridge(), "Why don't we have a bridge object here?");
    292     writeItem(vector, item);
    293     writeChildrenRecursive(vector, item);
    294 }
    295 
    296 void WebHistoryItem::updateHistoryItem(WebCore::HistoryItem* item) {
    297     // Do not want to update during inflation.
    298     if (!m_active)
    299         return;
    300     WebHistoryItem* webItem = this;
    301     // Now we need to update the top-most WebHistoryItem based on the top-most
    302     // HistoryItem.
    303     if (m_parent) {
    304         webItem = m_parent.get();
    305         if (webItem->hasOneRef()) {
    306             // if the parent only has one ref, it is from this WebHistoryItem.
    307             // This means that the matching WebCore::HistoryItem has been freed.
    308             // This can happen during clear().
    309             ALOGW("Can't updateHistoryItem as the top HistoryItem is gone");
    310             return;
    311         }
    312         while (webItem->parent())
    313             webItem = webItem->parent();
    314         item = webItem->historyItem();
    315         if (!item) {
    316             // If a HistoryItem only exists for page cache, it is possible that
    317             // the parent HistoryItem destroyed before the child HistoryItem. If
    318             // it happens, skip updating.
    319             ALOGW("Can't updateHistoryItem as the top HistoryItem is gone");
    320             return;
    321         }
    322     }
    323     JNIEnv* env = JSC::Bindings::getJNIEnv();
    324     if (!env)
    325         return;
    326 
    327     MutexLocker locker(webItem->m_lock);
    328 
    329     // TODO: Figure out if we can't just use item->urlString() instead...
    330     const WTF::String urlString = WebFrame::convertIDNToUnicode(item->url());
    331     webItem->m_url = urlString.threadsafeCopy();
    332     const WTF::String originalUrlString = WebFrame::convertIDNToUnicode(item->originalURL());
    333     webItem->m_originalUrl = originalUrlString.threadsafeCopy();
    334     const WTF::String& titleString = item->title();
    335     webItem->m_title = titleString.threadsafeCopy();
    336 
    337     // Try to get the favicon from the history item. For some pages like Grand
    338     // Prix, there are history items with anchors. If the icon fails for the
    339     // item, try to get the icon using the url without the ref.
    340     jobject favicon = NULL;
    341     WTF::String url = item->urlString();
    342     if (item->url().hasFragmentIdentifier()) {
    343         int refIndex = url.reverseFind('#');
    344         url = url.substring(0, refIndex);
    345     }
    346     // FIXME: This method should not be used from outside WebCore and will be removed.
    347     // http://trac.webkit.org/changeset/81484
    348     WebCore::Image* icon = WebCore::iconDatabase().synchronousIconForPageURL(url, WebCore::IntSize(16, 16));
    349     delete webItem->m_favicon;
    350     webItem->m_favicon = webcoreImageToSkBitmap(icon);
    351     if (webItem->m_faviconCached) {
    352         env->DeleteGlobalRef(webItem->m_faviconCached);
    353         webItem->m_faviconCached = 0;
    354     }
    355 
    356     webItem->m_data.clear();
    357     WebHistory::Flatten(env, webItem->m_data, item);
    358     if (webItem->m_dataCached) {
    359         env->DeleteGlobalRef(webItem->m_dataCached);
    360         webItem->m_dataCached = 0;
    361     }
    362 }
    363 
    364 WebHistoryItem::~WebHistoryItem()
    365 {
    366     delete m_favicon;
    367     JNIEnv* env = JSC::Bindings::getJNIEnv();
    368     if (!env) {
    369         ALOGW("Failed to get JNIEnv*! Potential memory leak!");
    370         return;
    371     }
    372     if (m_faviconCached) {
    373         env->DeleteGlobalRef(m_faviconCached);
    374         m_faviconCached = 0;
    375     }
    376     if (m_dataCached) {
    377         env->DeleteGlobalRef(m_dataCached);
    378         m_dataCached = 0;
    379     }
    380 }
    381 
    382 static void historyItemChanged(WebCore::HistoryItem* item) {
    383     ALOG_ASSERT(item, "historyItemChanged called with a null item");
    384 
    385     if (item->bridge())
    386         item->bridge()->updateHistoryItem(item);
    387 }
    388 
    389 void WebHistory::AddItem(const AutoJObject& list, WebCore::HistoryItem* item)
    390 {
    391     ALOG_ASSERT(item, "newItem must take a valid HistoryItem!");
    392     // Item already added. Should only happen when we are inflating the list.
    393     if (item->bridge() || !list.get())
    394         return;
    395 
    396     JNIEnv* env = list.env();
    397     // Create the bridge, make it active, and attach it to the item.
    398     WebHistoryItem* bridge = new WebHistoryItem(item);
    399     bridge->setActive();
    400     item->setBridge(bridge);
    401     // Allocate a blank WebHistoryItemClassic
    402     jclass clazz = env->FindClass("android/webkit/WebHistoryItemClassic");
    403     jobject newItem = env->NewObject(clazz, gWebHistoryItemClassic.mInit,
    404             reinterpret_cast<int>(bridge));
    405     env->DeleteLocalRef(clazz);
    406 
    407     // Update the history item which will flatten the data and call update on
    408     // the java item.
    409     bridge->updateHistoryItem(item);
    410 
    411     // Add it to the list.
    412     env->CallVoidMethod(list.get(), gWebBackForwardListClassic.mAddHistoryItem, newItem);
    413 
    414     // Delete our local reference.
    415     env->DeleteLocalRef(newItem);
    416 }
    417 
    418 void WebHistory::RemoveItem(const AutoJObject& list, int index)
    419 {
    420     if (list.get())
    421         list.env()->CallVoidMethod(list.get(), gWebBackForwardListClassic.mRemoveHistoryItem, index);
    422 }
    423 
    424 void WebHistory::UpdateHistoryIndex(const AutoJObject& list, int newIndex)
    425 {
    426     if (list.get())
    427         list.env()->CallVoidMethod(list.get(), gWebBackForwardListClassic.mSetCurrentIndex, newIndex);
    428 }
    429 
    430 static void writeString(WTF::Vector<char>& vector, const WTF::String& str)
    431 {
    432     unsigned strLen = str.length();
    433     // Only do work if the string has data.
    434     if (strLen) {
    435         // Determine how much to grow the vector. Use the worst case for utf8 to
    436         // avoid reading the string twice. Add sizeof(unsigned) to hold the
    437         // string length in utf8.
    438         unsigned vectorLen = vector.size() + sizeof(unsigned);
    439         unsigned length = (strLen << 2) + vectorLen;
    440         // Grow the vector. This will change the value of v.size() but we
    441         // remember the original size above.
    442         vector.grow(length);
    443         // Grab the position to write to.
    444         char* data = vector.begin() + vectorLen;
    445         // Write the actual string
    446         int l = SkUTF16_ToUTF8(str.characters(), strLen, data);
    447         ALOGV("Writing string          %d %.*s", l, l, data);
    448         // Go back and write the utf8 length. Subtract sizeof(unsigned) from
    449         // data to get the position to write the length.
    450         memcpy(data - sizeof(unsigned), (char*)&l, sizeof(unsigned));
    451         // Shrink the internal state of the vector so we match what was
    452         // actually written.
    453         vector.shrink(vectorLen + l);
    454     } else
    455         vector.append((char*)&strLen, sizeof(unsigned));
    456 }
    457 
    458 static void writeItem(WTF::Vector<char>& vector, WebCore::HistoryItem* item)
    459 {
    460     // Original url
    461     writeString(vector, item->originalURLString());
    462 
    463     // Url
    464     writeString(vector, item->urlString());
    465 
    466     // Title
    467     writeString(vector, item->title());
    468 
    469     // Form content type
    470     writeString(vector, item->formContentType());
    471 
    472     // Form data
    473     const WebCore::FormData* formData = item->formData();
    474     if (formData) {
    475         WTF::String flattenedFormData = formData->flattenToString();
    476         writeString(vector, flattenedFormData);
    477         if (!flattenedFormData.isEmpty()) {
    478             // save the identifier as it is not included in the flatten data
    479             int64_t id = formData->identifier();
    480             vector.append((char*)&id, sizeof(int64_t));
    481         }
    482     } else
    483         writeString(vector, WTF::String()); // Empty constructor does not allocate a buffer.
    484 
    485     // Target
    486     writeString(vector, item->target());
    487 
    488     AndroidWebHistoryBridge* bridge = item->bridge();
    489     ALOG_ASSERT(bridge, "We should have a bridge here!");
    490     // Screen scale
    491     const float scale = bridge->scale();
    492     ALOGV("Writing scale           %f", scale);
    493     vector.append((char*)&scale, sizeof(float));
    494     const float textWrapScale = bridge->textWrapScale();
    495     ALOGV("Writing text wrap scale %f", textWrapScale);
    496     vector.append((char*)&textWrapScale, sizeof(float));
    497 
    498     // Scroll position.
    499     const int scrollX = item->scrollPoint().x();
    500     vector.append((char*)&scrollX, sizeof(int));
    501     const int scrollY = item->scrollPoint().y();
    502     vector.append((char*)&scrollY, sizeof(int));
    503 
    504     // Document state
    505     const WTF::Vector<WTF::String>& docState = item->documentState();
    506     WTF::Vector<WTF::String>::const_iterator end = docState.end();
    507     unsigned stateSize = docState.size();
    508     ALOGV("Writing docState        %d", stateSize);
    509     vector.append((char*)&stateSize, sizeof(unsigned));
    510     for (WTF::Vector<WTF::String>::const_iterator i = docState.begin(); i != end; ++i) {
    511         writeString(vector, *i);
    512     }
    513 
    514     // Is target item
    515     ALOGV("Writing isTargetItem    %d", item->isTargetItem());
    516     vector.append((char)item->isTargetItem());
    517 
    518     // Children count
    519     unsigned childCount = item->children().size();
    520     ALOGV("Writing childCount      %d", childCount);
    521     vector.append((char*)&childCount, sizeof(unsigned));
    522 }
    523 
    524 static void writeChildrenRecursive(WTF::Vector<char>& vector, WebCore::HistoryItem* parent)
    525 {
    526     const WebCore::HistoryItemVector& children = parent->children();
    527     WebCore::HistoryItemVector::const_iterator end = children.end();
    528     for (WebCore::HistoryItemVector::const_iterator i = children.begin(); i != end; ++i) {
    529         WebCore::HistoryItem* item = (*i).get();
    530         ALOG_ASSERT(parent->bridge(),
    531                 "The parent item should have a bridge object!");
    532         if (!item->bridge()) {
    533             WebHistoryItem* bridge = new WebHistoryItem(static_cast<WebHistoryItem*>(parent->bridge()));
    534             item->setBridge(bridge);
    535             bridge->setActive();
    536         } else {
    537             // The only time this item's parent may not be the same as the
    538             // parent's bridge is during history close. In that case, the
    539             // parent must not have a parent bridge.
    540             WebHistoryItem* bridge = static_cast<WebHistoryItem*>(item->bridge());
    541             WebHistoryItem* parentBridge = static_cast<WebHistoryItem*>(parent->bridge());
    542             ALOG_ASSERT(parentBridge->parent() == 0 ||
    543                     bridge->parent() == parentBridge,
    544                     "Somehow this item has an incorrect parent");
    545             bridge->setParent(parentBridge);
    546         }
    547         writeItem(vector, item);
    548         writeChildrenRecursive(vector, item);
    549     }
    550 }
    551 
    552 bool readUnsigned(const char*& data, const char* end, unsigned& result, const char* dbgLabel = 0);
    553 bool readInt(const char*& data, const char* end, int& result, const char* dbgLabel = 0);
    554 bool readInt64(const char*& data, const char* end, int64_t& result, const char* dbgLabel = 0);
    555 bool readFloat(const char*& data, const char* end, float& result, const char* dbgLabel = 0);
    556 bool readBool(const char*& data, const char* end, bool& result, const char* dbgLabel = 0);
    557 bool readString(const char*& data, const char* end, String& result, const char* dbgLabel = 0);
    558 
    559 bool readUnsigned(const char*& data, const char* end, unsigned& result, const char* dbgLabel)
    560 {
    561     // Check if we have enough data left to continue.
    562     if ((end < data) || (static_cast<size_t>(end - data) < sizeof(unsigned))) {
    563         ALOGW("\tNot enough data to read unsigned; tag=\"%s\" end=%p data=%p",
    564               dbgLabel ? dbgLabel : "<no tag>", end, data);
    565         return false;
    566     }
    567 
    568     memcpy(&result, data, sizeof(unsigned));
    569     data += sizeof(unsigned);
    570     if (dbgLabel)
    571         ALOGV("Reading %-16s %u", dbgLabel, result);
    572     return true;
    573 }
    574 
    575 bool readInt(const char*& data, const char* end, int& result, const char* dbgLabel)
    576 {
    577     // Check if we have enough data left to continue.
    578     if ((end < data) || (static_cast<size_t>(end - data) < sizeof(int))) {
    579         ALOGW("Not enough data to read int; tag=\"%s\" end=%p data=%p",
    580               dbgLabel ? dbgLabel : "<no tag>", end, data);
    581         return false;
    582     }
    583 
    584     memcpy(&result, data, sizeof(int));
    585     data += sizeof(int);
    586     if (dbgLabel)
    587         ALOGV("Reading %-16s %d", dbgLabel, result);
    588     return true;
    589 }
    590 
    591 bool readInt64(const char*& data, const char* end, int64_t& result, const char* dbgLabel)
    592 {
    593     // Check if we have enough data left to continue.
    594     if ((end < data) || (static_cast<size_t>(end - data) < sizeof(int64_t))) {
    595         ALOGW("Not enough data to read int64_t; tag=\"%s\" end=%p data=%p",
    596               dbgLabel ? dbgLabel : "<no tag>", end, data);
    597         return false;
    598     }
    599 
    600     memcpy(&result, data, sizeof(int64_t));
    601     data += sizeof(int64_t);
    602     if (dbgLabel)
    603         ALOGV("Reading %-16s %ll", dbgLabel, result);
    604     return true;
    605 }
    606 
    607 bool readFloat(const char*& data, const char* end, float& result, const char* dbgLabel)
    608 {
    609     // Check if we have enough data left to continue.
    610     if ((end < data) || (static_cast<size_t>(end - data) < sizeof(float))) {
    611         ALOGW("Not enough data to read float; tag=\"%s\" end=%p data=%p",
    612               dbgLabel ? dbgLabel : "<no tag>", end, data);
    613         return false;
    614     }
    615 
    616     memcpy(&result, data, sizeof(float));
    617     data += sizeof(float);
    618     if (dbgLabel)
    619         ALOGV("Reading %-16s %f", dbgLabel, result);
    620     return true;
    621 }
    622 
    623 // Note that the return value indicates success or failure, while the result
    624 // parameter indicates the read value of the bool
    625 bool readBool(const char*& data, const char* end, bool& result, const char* dbgLabel)
    626 {
    627     // Check if we have enough data left to continue.
    628     if ((end < data) || (static_cast<size_t>(end - data) < sizeof(char))) {
    629         ALOGW("Not enough data to read bool; tag=\"%s\" end=%p data=%p",
    630               dbgLabel ? dbgLabel : "<no tag>", end, data);
    631         return false;
    632     }
    633 
    634     char c;
    635     memcpy(&c, data, sizeof(char));
    636     data += sizeof(char);
    637     if (dbgLabel)
    638         ALOGV("Reading %-16s %d", dbgLabel, c);
    639     result = c;
    640 
    641     // Valid bool results are 0 or 1
    642     if ((c != 0) && (c != 1)) {
    643         ALOGW("Invalid value for bool; tag=\"%s\" end=%p data=%p c=%u",
    644               dbgLabel ? dbgLabel : "<no tag>", end, data, c);
    645         return false;
    646     }
    647 
    648     return true;
    649 }
    650 
    651 bool readString(const char*& data, const char* end, String& result, const char* dbgLabel)
    652 {
    653     unsigned stringLength;
    654     if (!readUnsigned(data, end, stringLength)) {
    655         ALOGW("Not enough data to read string length; tag=\"%s\" end=%p data=%p",
    656               dbgLabel ? dbgLabel : "<no tag>", end, data);
    657         return false;
    658     }
    659 
    660     if (dbgLabel)
    661         ALOGV("Reading %-16s %d %.*s", dbgLabel, stringLength, stringLength, data);
    662 
    663     // If length was 0, there will be no string content, but still return true
    664     if (!stringLength) {
    665         result = String();
    666         return true;
    667     }
    668 
    669     if ((end < data) || ((unsigned)(end - data) < stringLength)) {
    670         ALOGW("Not enough data to read content; tag=\"%s\" end=%p data=%p stringLength=%u",
    671               dbgLabel ? dbgLabel : "<no tag>", end, data, stringLength);
    672         return false;
    673     }
    674 
    675     const unsigned MAX_REASONABLE_STRING_LENGTH = 10000;
    676     if (stringLength > MAX_REASONABLE_STRING_LENGTH) {
    677         ALOGW("String length is suspiciously large (>%d); tag=\"%s\" end=%p data=%p stringLength=%u",
    678               MAX_REASONABLE_STRING_LENGTH, dbgLabel ? dbgLabel : "<no tag>",
    679               end, data, stringLength);
    680     }
    681 
    682     bool decodeFailed = false;
    683     static const WebCore::TextEncoding& encoding = WebCore::UTF8Encoding();
    684     result = encoding.decode(data, stringLength, true, decodeFailed);
    685     if (decodeFailed) {
    686         ALOGW("Decode failed, tag=\"%s\" end=%p data=%p stringLength=%u content=\"%s\"",
    687               dbgLabel ? dbgLabel : "<no tag>", end, data, stringLength,
    688               result.utf8().data());
    689         return false;
    690     }
    691 
    692     if (stringLength > MAX_REASONABLE_STRING_LENGTH) {
    693         ALOGW("\tdecodeFailed=%d (flag is ignored) content=\"%s\"",
    694               decodeFailed, result.utf8().data());
    695     }
    696 
    697     data += stringLength;
    698     return true;
    699 }
    700 
    701 static bool readItemRecursive(WebCore::HistoryItem* newItem,
    702         const char** pData, int length)
    703 {
    704     if (!pData || length < HISTORY_MIN_SIZE) {
    705         ALOGW("readItemRecursive() bad params; pData=%p length=%d", pData, length);
    706         return false;
    707     }
    708 
    709     const char* data = *pData;
    710     const char* end = data + length;
    711     String content;
    712 
    713     // Read the original url
    714     if (readString(data, end, content, "Original url"))
    715         newItem->setOriginalURLString(content);
    716     else
    717         return false;
    718 
    719     // Read the url
    720     if (readString(data, end, content, "Url"))
    721         newItem->setURLString(content);
    722     else
    723         return false;
    724 
    725     // Read the title
    726     if (readString(data, end, content, "Title"))
    727         newItem->setTitle(content);
    728     else
    729         return false;
    730 
    731     // Generate a new ResourceRequest object for populating form information.
    732     // Read the form content type
    733     WTF::String formContentType;
    734     if (!readString(data, end, formContentType, "Content type"))
    735         return false;
    736 
    737     // Read the form data size
    738     unsigned formDataSize;
    739     if (!readUnsigned(data, end, formDataSize, "Form data size"))
    740         return false;
    741 
    742     // Read the form data
    743     WTF::RefPtr<WebCore::FormData> formData;
    744     if (formDataSize) {
    745         ALOGV("Reading Form data       %d %.*s", formDataSize, formDataSize, data);
    746         if ((end < data) || ((size_t)(end - data) < formDataSize)) {
    747             ALOGW("\tNot enough data to read form data; returning");
    748             return false;
    749         }
    750         formData = WebCore::FormData::create(data, formDataSize);
    751         data += formDataSize;
    752         // Read the identifier
    753         int64_t id;
    754         if (!readInt64(data, end, id, "Form id"))
    755             return false;
    756         if (id)
    757             formData->setIdentifier(id);
    758     }
    759 
    760     // Set up the form info
    761     if (formData != NULL) {
    762         WebCore::ResourceRequest r;
    763         r.setHTTPMethod("POST");
    764         r.setHTTPContentType(formContentType);
    765         r.setHTTPBody(formData);
    766         newItem->setFormInfoFromRequest(r);
    767     }
    768 
    769     // Read the target
    770     if (readString(data, end, content, "Target"))
    771         newItem->setTarget(content);
    772     else
    773         return false;
    774 
    775     AndroidWebHistoryBridge* bridge = newItem->bridge();
    776     ALOG_ASSERT(bridge, "There should be a bridge object during inflate");
    777 
    778     // Read the screen scale
    779     float fValue;
    780     if (readFloat(data, end, fValue, "Screen scale"))
    781         bridge->setScale(fValue);
    782     else
    783         return false;
    784 
    785     // Read the text wrap scale
    786     if (readFloat(data, end, fValue, "Text wrap scale"))
    787         bridge->setTextWrapScale(fValue);
    788     else
    789         return false;
    790 
    791     // Read scroll position.
    792     int scrollX;
    793     if (!readInt(data, end, scrollX, "Scroll pos x"))
    794         return false;
    795     int scrollY;
    796     if (!readInt(data, end, scrollY, "Scroll pos y"))
    797         return false;
    798     newItem->setScrollPoint(IntPoint(scrollX, scrollY));
    799 
    800     // Read the document state
    801     unsigned docStateCount;
    802     if (!readUnsigned(data, end, docStateCount, "Doc state count"))
    803         return false;
    804     if (docStateCount) {
    805         // Create a new vector and reserve enough space for the document state.
    806         WTF::Vector<WTF::String> docState;
    807         docState.reserveCapacity(docStateCount);
    808         while (docStateCount--) {
    809             // Read a document state string
    810             if (readString(data, end, content, "Document state"))
    811                 docState.append(content);
    812             else
    813                 return false;
    814         }
    815         newItem->setDocumentState(docState);
    816     }
    817 
    818     // Read is target item
    819     bool c;
    820     if (readBool(data, end, c, "Target item"))
    821         newItem->setIsTargetItem(c);
    822     else
    823         return false;
    824 
    825     // Read the child count
    826     unsigned count;
    827     if (!readUnsigned(data, end, count, "Child count"))
    828         return false;
    829     *pData = data;
    830     if (count) {
    831         while (count--) {
    832             // No need to check the length each time because read_item_recursive
    833             // will return null if there isn't enough data left to parse.
    834             WTF::RefPtr<WebCore::HistoryItem> child = WebCore::HistoryItem::create();
    835             // Set a bridge that will not call into java.
    836             child->setBridge(new WebHistoryItem(static_cast<WebHistoryItem*>(bridge)));
    837             // Read the child item.
    838             if (!readItemRecursive(child.get(), pData, end - data))
    839                 return false;
    840             child->bridge()->setActive();
    841             newItem->addChildItem(child);
    842         }
    843     }
    844     return true;
    845 }
    846 
    847 // On arm, this test will cause memory corruption since converting char* will
    848 // byte align the result and this test does not use memset (it probably
    849 // should).
    850 // On the simulator, using HistoryItem will invoke the IconDatabase which will
    851 // initialize the main thread. Since this is invoked by the Zygote process, the
    852 // main thread will be incorrect and an assert will fire later.
    853 // In conclusion, define UNIT_TEST only if you know what you are doing.
    854 #ifdef UNIT_TEST
    855 static void unitTest()
    856 {
    857     ALOGD("Entering history unit test!");
    858     const char* test1 = new char[0];
    859     WTF::RefPtr<WebCore::HistoryItem> item = WebCore::HistoryItem::create();
    860     WebCore::HistoryItem* testItem = item.get();
    861     testItem->setBridge(new WebHistoryItem(0));
    862     ALOG_ASSERT(!readItemRecursive(testItem, &test1, 0), "0 length array should fail!");
    863     delete[] test1;
    864     const char* test2 = new char[2];
    865     ALOG_ASSERT(!readItemRecursive(testItem, &test2, 2), "Small array should fail!");
    866     delete[] test2;
    867     ALOG_ASSERT(!readItemRecursive(testItem, NULL, HISTORY_MIN_SIZE), "Null data should fail!");
    868     // Original Url
    869     char* test3 = new char[HISTORY_MIN_SIZE];
    870     const char* ptr = (const char*)test3;
    871     memset(test3, 0, HISTORY_MIN_SIZE);
    872     *(int*)test3 = 4000;
    873     ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length originalUrl should fail!");
    874     // Url
    875     int offset = 4;
    876     memset(test3, 0, HISTORY_MIN_SIZE);
    877     ptr = (const char*)test3;
    878     *(int*)(test3 + offset) = 4000;
    879     ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length url should fail!");
    880     // Title
    881     offset += 4;
    882     memset(test3, 0, HISTORY_MIN_SIZE);
    883     ptr = (const char*)test3;
    884     *(int*)(test3 + offset) = 4000;
    885     ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length title should fail!");
    886     // Form content type
    887     offset += 4;
    888     memset(test3, 0, HISTORY_MIN_SIZE);
    889     ptr = (const char*)test3;
    890     *(int*)(test3 + offset) = 4000;
    891     ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length contentType should fail!");
    892     // Form data
    893     offset += 4;
    894     memset(test3, 0, HISTORY_MIN_SIZE);
    895     ptr = (const char*)test3;
    896     *(int*)(test3 + offset) = 4000;
    897     ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length form data should fail!");
    898     // Target
    899     offset += 4;
    900     memset(test3, 0, HISTORY_MIN_SIZE);
    901     ptr = (const char*)test3;
    902     *(int*)(test3 + offset) = 4000;
    903     ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length target should fail!");
    904     offset += 4; // Screen scale
    905     offset += 4; // Text wrap scale
    906     offset += 4; // Scroll pos x
    907     offset += 4; // Scroll pos y
    908     // Document state
    909     offset += 4;
    910     memset(test3, 0, HISTORY_MIN_SIZE);
    911     ptr = (const char*)test3;
    912     *(int*)(test3 + offset) = 4000;
    913     ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length document state should fail!");
    914     // Is target item
    915     offset += 1;
    916     memset(test3, 0, HISTORY_MIN_SIZE);
    917     ptr = (const char*)test3;
    918     *(char*)(test3 + offset) = '!';
    919     ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "IsTargetItem should fail with ! as the value!");
    920     // Child count
    921     offset += 4;
    922     memset(test3, 0, HISTORY_MIN_SIZE);
    923     ptr = (const char*)test3;
    924     *(int*)(test3 + offset) = 4000;
    925     ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 kids should fail!");
    926     // Test document state
    927     offset = 40;
    928     delete[] test3;
    929     test3 = new char[HISTORY_MIN_SIZE + sizeof(unsigned)];
    930     memset(test3, 0, HISTORY_MIN_SIZE + sizeof(unsigned));
    931     ptr = (const char*)test3;
    932     *(int*)(test3 + offset) = 1;
    933     *(int*)(test3 + offset + 4) = 20;
    934     ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE + sizeof(unsigned)), "1 20 length document state string should fail!");
    935     delete[] test3;
    936     test3 = new char[HISTORY_MIN_SIZE + 2 * sizeof(unsigned)];
    937     memset(test3, 0, HISTORY_MIN_SIZE + 2 * sizeof(unsigned));
    938     ptr = (const char*)test3;
    939     *(int*)(test3 + offset) = 2;
    940     *(int*)(test3 + offset + 4) = 0;
    941     *(int*)(test3 + offset + 8) = 20;
    942     ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE + 2 * sizeof(unsigned) ), "2 20 length document state string should fail!");
    943     delete[] test3;
    944     ALOGD("Leaving history unit test!");
    945 }
    946 #endif
    947 
    948 //---------------------------------------------------------
    949 // JNI registration
    950 //---------------------------------------------------------
    951 static JNINativeMethod gWebBackForwardListClassicMethods[] = {
    952     { "nativeClose", "(I)V",
    953         (void*) WebHistoryClose },
    954     { "restoreIndex", "(II)V",
    955         (void*) WebHistoryRestoreIndex }
    956 };
    957 
    958 static JNINativeMethod gWebHistoryItemClassicMethods[] = {
    959     { "inflate", "(I[B)I",
    960         (void*) WebHistoryInflate },
    961     { "nativeRef", "(I)V",
    962         (void*) WebHistoryRef },
    963     { "nativeUnref", "(I)V",
    964         (void*) WebHistoryUnref },
    965     { "nativeGetTitle", "(I)Ljava/lang/String;",
    966         (void*) WebHistoryGetTitle },
    967     { "nativeGetUrl", "(I)Ljava/lang/String;",
    968         (void*) WebHistoryGetUrl },
    969     { "nativeGetOriginalUrl", "(I)Ljava/lang/String;",
    970         (void*) WebHistoryGetOriginalUrl },
    971     { "nativeGetFlattenedData", "(I)[B",
    972         (void*) WebHistoryGetFlattenedData },
    973     { "nativeGetFavicon", "(I)Landroid/graphics/Bitmap;",
    974         (void*) WebHistoryGetFavicon },
    975 };
    976 
    977 int registerWebHistory(JNIEnv* env)
    978 {
    979     // Get notified of all changes to history items.
    980     WebCore::notifyHistoryItemChanged = historyItemChanged;
    981 #ifdef UNIT_TEST
    982     unitTest();
    983 #endif
    984     // Find WebHistoryItemClassic, its constructor, and the update method.
    985     jclass clazz = env->FindClass("android/webkit/WebHistoryItemClassic");
    986     ALOG_ASSERT(clazz, "Unable to find class android/webkit/WebHistoryItemClassic");
    987     gWebHistoryItemClassic.mInit = env->GetMethodID(clazz, "<init>", "(I)V");
    988     ALOG_ASSERT(gWebHistoryItemClassic.mInit, "Could not find WebHistoryItemClassic constructor");
    989     env->DeleteLocalRef(clazz);
    990 
    991     // Find the WebBackForwardListClassic object and method.
    992     clazz = env->FindClass("android/webkit/WebBackForwardListClassic");
    993     ALOG_ASSERT(clazz, "Unable to find class android/webkit/WebBackForwardListClassic");
    994     gWebBackForwardListClassic.mAddHistoryItem = env->GetMethodID(clazz, "addHistoryItem",
    995             "(Landroid/webkit/WebHistoryItem;)V");
    996     ALOG_ASSERT(gWebBackForwardListClassic.mAddHistoryItem, "Could not find method addHistoryItem");
    997     gWebBackForwardListClassic.mRemoveHistoryItem = env->GetMethodID(clazz, "removeHistoryItem",
    998             "(I)V");
    999     ALOG_ASSERT(gWebBackForwardListClassic.mRemoveHistoryItem, "Could not find method removeHistoryItem");
   1000     gWebBackForwardListClassic.mSetCurrentIndex = env->GetMethodID(clazz, "setCurrentIndex", "(I)V");
   1001     ALOG_ASSERT(gWebBackForwardListClassic.mSetCurrentIndex, "Could not find method setCurrentIndex");
   1002     env->DeleteLocalRef(clazz);
   1003 
   1004     int result = jniRegisterNativeMethods(env, "android/webkit/WebBackForwardListClassic",
   1005             gWebBackForwardListClassicMethods, NELEM(gWebBackForwardListClassicMethods));
   1006     return (result < 0) ? result : jniRegisterNativeMethods(env, "android/webkit/WebHistoryItemClassic",
   1007             gWebHistoryItemClassicMethods, NELEM(gWebHistoryItemClassicMethods));
   1008 }
   1009 
   1010 } /* namespace android */
   1011