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
56namespace android {
57
58// Forward declarations
59static void writeItem(WTF::Vector<char>& vector, WebCore::HistoryItem* item);
60static void writeChildrenRecursive(WTF::Vector<char>& vector, WebCore::HistoryItem* parent);
61static bool readItemRecursive(WebCore::HistoryItem* child, const char** pData, int length);
62
63// Field ids for WebHistoryClassicItems
64struct WebHistoryItemClassicFields {
65    jmethodID   mInit;
66} gWebHistoryItemClassic;
67
68struct WebBackForwardListClassicFields {
69    jmethodID   mAddHistoryItem;
70    jmethodID   mRemoveHistoryItem;
71    jmethodID   mSetCurrentIndex;
72} gWebBackForwardListClassic;
73
74//--------------------------------------------------------------------------
75// WebBackForwardListClassic native methods.
76//--------------------------------------------------------------------------
77
78static 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
145static 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
158static 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
198static void WebHistoryRef(JNIEnv* env, jobject obj, jint ptr)
199{
200    if (ptr)
201        reinterpret_cast<WebHistoryItem*>(ptr)->ref();
202}
203
204static void WebHistoryUnref(JNIEnv* env, jobject obj, jint ptr)
205{
206    if (ptr)
207        reinterpret_cast<WebHistoryItem*>(ptr)->deref();
208}
209
210static 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
219static 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
228static 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
237static 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
260static 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
281void 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
296void 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
364WebHistoryItem::~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
382static 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
389void 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
418void WebHistory::RemoveItem(const AutoJObject& list, int index)
419{
420    if (list.get())
421        list.env()->CallVoidMethod(list.get(), gWebBackForwardListClassic.mRemoveHistoryItem, index);
422}
423
424void WebHistory::UpdateHistoryIndex(const AutoJObject& list, int newIndex)
425{
426    if (list.get())
427        list.env()->CallVoidMethod(list.get(), gWebBackForwardListClassic.mSetCurrentIndex, newIndex);
428}
429
430static 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
458static 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
524static 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
552bool readUnsigned(const char*& data, const char* end, unsigned& result, const char* dbgLabel = 0);
553bool readInt(const char*& data, const char* end, int& result, const char* dbgLabel = 0);
554bool readInt64(const char*& data, const char* end, int64_t& result, const char* dbgLabel = 0);
555bool readFloat(const char*& data, const char* end, float& result, const char* dbgLabel = 0);
556bool readBool(const char*& data, const char* end, bool& result, const char* dbgLabel = 0);
557bool readString(const char*& data, const char* end, String& result, const char* dbgLabel = 0);
558
559bool 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
575bool 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
591bool 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
607bool 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
625bool 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
651bool 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
701static 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
855static 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//---------------------------------------------------------
951static JNINativeMethod gWebBackForwardListClassicMethods[] = {
952    { "nativeClose", "(I)V",
953        (void*) WebHistoryClose },
954    { "restoreIndex", "(II)V",
955        (void*) WebHistoryRestoreIndex }
956};
957
958static 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
977int 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