• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 APPLE COMPUTER, INC. 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 <wtf/OwnPtr.h>
30 #include <wtf/Platform.h>
31 
32 #include "WebHistory.h"
33 
34 #include "BackForwardList.h"
35 #include "CString.h"
36 #include "DocumentLoader.h"
37 #include "Frame.h"
38 #include "FrameLoader.h"
39 #include "FrameLoaderClientAndroid.h"
40 #include "FrameTree.h"
41 #include "HistoryItem.h"
42 #include "IconDatabase.h"
43 #include "Page.h"
44 #include "TextEncoding.h"
45 #include "WebCoreFrameBridge.h"
46 #include "WebCoreJni.h"
47 #include "jni_utility.h"
48 
49 #include <JNIHelp.h>
50 #include <SkUtils.h>
51 #include <utils/misc.h>
52 
53 namespace android {
54 
55 // Forward declarations
56 static void write_item(WTF::Vector<char>& v, WebCore::HistoryItem* item);
57 static void write_children_recursive(WTF::Vector<char>& v, WebCore::HistoryItem* parent);
58 static bool read_item_recursive(WebCore::HistoryItem* child, const char** pData, int length);
59 
60 // Field ids for WebHistoryItems
61 struct WebHistoryItemFields {
62     jmethodID   mInit;
63     jmethodID   mUpdate;
64     jfieldID    mTitle;
65     jfieldID    mUrl;
66 } gWebHistoryItem;
67 
68 struct WebBackForwardListFields {
69     jmethodID   mAddHistoryItem;
70     jmethodID   mRemoveHistoryItem;
71     jfieldID    mCurrentIndex;
72 } gWebBackForwardList;
73 
74 //--------------------------------------------------------------------------
75 // WebBackForwardList native methods.
76 //--------------------------------------------------------------------------
77 
WebHistoryClose(JNIEnv * env,jobject obj,jint frame)78 static void WebHistoryClose(JNIEnv* env, jobject obj, jint frame)
79 {
80     LOG_ASSERT(frame, "Close needs a valid Frame pointer!");
81     WebCore::Frame* pFrame = (WebCore::Frame*)frame;
82 
83     WebCore::BackForwardList* list = 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(NULL);
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()->setCurrentHistoryItem(current);
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()->setCurrentHistoryItem(item);
125             // Append the first child to the queue if it exists.
126             if (WebCore::Frame* f = child->tree()->firstChild())
127                 frameQueue.append(f);
128             child = child->tree()->nextSibling();
129             // If we don't have a sibling for this frame and the queue isn't
130             // empty, use the next entry in the queue.
131             if (!child && !frameQueue.isEmpty()) {
132                 child = frameQueue.at(0);
133                 frameQueue.remove(0);
134                 // Figure out the parent history item used when searching for
135                 // the history item to use.
136                 parent = child->tree()->parent()->loader()->currentHistoryItem();
137             }
138         }
139     }
140 }
141 
WebHistoryRestoreIndex(JNIEnv * env,jobject obj,jint frame,jint index)142 static void WebHistoryRestoreIndex(JNIEnv* env, jobject obj, jint frame, jint index)
143 {
144     LOG_ASSERT(frame, "RestoreState needs a valid Frame pointer!");
145     WebCore::Frame* pFrame = (WebCore::Frame*)frame;
146     WebCore::Page* page = pFrame->page();
147 
148     // Set the current index in the list.
149     WebCore::BackForwardList* list = page->backForwardList();
150     WebCore::HistoryItem* currentItem = list->entries()[index].get();
151     list->goToItem(currentItem);
152 
153     // Update the current and previous history item.
154     WebCore::FrameLoader* loader = pFrame->loader();
155     loader->setCurrentHistoryItem(currentItem);
156 
157     // load the current page with FrameLoadTypeIndexedBackForward so that it
158     // will use cache when it is possible
159     page->goToItem(currentItem, FrameLoadTypeIndexedBackForward);
160 }
161 
WebHistoryInflate(JNIEnv * env,jobject obj,jint frame,jbyteArray data)162 static void WebHistoryInflate(JNIEnv* env, jobject obj, jint frame, jbyteArray data)
163 {
164     LOG_ASSERT(frame, "Inflate needs a valid frame pointer!");
165     LOG_ASSERT(data, "Inflate needs a valid data pointer!");
166 
167     // Get the actual bytes and the length from the java array.
168     const jbyte* bytes = env->GetByteArrayElements(data, NULL);
169     jsize size = env->GetArrayLength(data);
170 
171     // Inflate the history tree into one HistoryItem or null if the inflation
172     // failed.
173     RefPtr<WebCore::HistoryItem> newItem = WebCore::HistoryItem::create();
174 #ifdef ANDROID_HISTORY_CLIENT
175     RefPtr<WebHistoryItem> bridge = new WebHistoryItem(env, obj, newItem.get());
176     newItem->setBridge(bridge.get());
177 #endif
178     // Inflate the item recursively. If it fails, that is ok. We'll have an
179     // incomplete HistoryItem but that is better than crashing due to a null
180     // item.
181     // We have a 2nd local variable since read_item_recursive may change the
182     // ptr's value. We can't pass &bytes since we have to send bytes to
183     // ReleaseByteArrayElements unchanged.
184     const char* ptr = reinterpret_cast<const char*>(bytes);
185     read_item_recursive(newItem.get(), &ptr, (int)size);
186     env->ReleaseByteArrayElements(data, const_cast<jbyte*>(bytes), JNI_ABORT);
187 #ifdef ANDROID_HISTORY_CLIENT
188     bridge->setActive();
189 #endif
190 
191     // Add the new item to the back/forward list.
192     WebCore::Frame* pFrame = (WebCore::Frame*)frame;
193     pFrame->page()->backForwardList()->addItem(newItem);
194 
195 #ifdef ANDROID_HISTORY_CLIENT
196     // Update the item.
197     bridge->updateHistoryItem(newItem.get());
198 #endif
199 }
200 
201 // 6 empty strings + no document state + children count = 8 unsigned values
202 // 1 char for isTargetItem
203 // ANDROID_HISTORY_CLIENT adds 1 int for scale.
204 #ifdef ANDROID_HISTORY_CLIENT
205 #define HISTORY_MIN_SIZE ((int)(sizeof(unsigned) * 9 + sizeof(char)))
206 #else
207 #define HISTORY_MIN_SIZE ((int)(sizeof(unsigned) * 8 + sizeof(char)))
208 #endif
209 
Flatten(JNIEnv * env,WTF::Vector<char> & v,WebCore::HistoryItem * item)210 jbyteArray WebHistory::Flatten(JNIEnv* env, WTF::Vector<char>& v, WebCore::HistoryItem* item)
211 {
212     if (!item)
213         return NULL;
214 
215     // Reserve a vector of chars with an initial size of HISTORY_MIN_SIZE.
216     v.reserveCapacity(HISTORY_MIN_SIZE);
217 
218     // Write the top-level history item and then write all the children
219     // recursively.
220 #ifdef ANDROID_HISTORY_CLIENT
221     LOG_ASSERT(item->bridge(), "Why don't we have a bridge object here?");
222 #endif
223     write_item(v, item);
224     write_children_recursive(v, item);
225 
226     // Try to create a new java byte array.
227     jbyteArray b = env->NewByteArray(v.size());
228     if (!b)
229         return NULL;
230 
231     // Write our flattened data to the java array.
232     env->SetByteArrayRegion(b, 0, v.size(), (const jbyte*)v.data());
233     return b;
234 }
235 
WebHistoryItem(JNIEnv * env,jobject obj,WebCore::HistoryItem * item)236 WebHistoryItem::WebHistoryItem(JNIEnv* env, jobject obj,
237         WebCore::HistoryItem* item) {
238     mObject = adoptGlobalRef(env, obj);
239     mScale = 100;
240     mActive = false;
241     mParent = NULL;
242     mHistoryItem = item;
243 }
244 
~WebHistoryItem()245 WebHistoryItem::~WebHistoryItem() {
246     if (mObject) {
247         JNIEnv* env = JSC::Bindings::getJNIEnv();
248         if (!env)
249             return;
250         env->DeleteGlobalRef(mObject);
251     }
252 }
253 
updateHistoryItem(WebCore::HistoryItem * item)254 void WebHistoryItem::updateHistoryItem(WebCore::HistoryItem* item) {
255 #ifdef ANDROID_HISTORY_CLIENT
256     // Do not want to update during inflation.
257     if (!mActive)
258         return;
259     WebHistoryItem* webItem = this;
260     // Now we need to update the top-most WebHistoryItem based on the top-most
261     // HistoryItem.
262     if (mParent) {
263         webItem = mParent.get();
264         if (webItem->hasOneRef()) {
265             // if the parent only has one ref, it is from this WebHistoryItem.
266             // This means that the matching WebCore::HistoryItem has been freed.
267             // This can happen during clear().
268             LOGW("Can't updateHistoryItem as the top HistoryItem is gone");
269             return;
270         }
271         while (webItem->parent())
272             webItem = webItem->parent();
273         item = webItem->historyItem();
274     }
275     JNIEnv* env = JSC::Bindings::getJNIEnv();
276     if (!env)
277         return;
278 
279     // Don't do anything if the item has been gc'd already
280     AutoJObject realItem = getRealObject(env, webItem->mObject);
281     if (!realItem.get())
282         return;
283 
284     const WebCore::String& urlString = item->urlString();
285     jstring urlStr = NULL;
286     if (!urlString.isNull())
287         urlStr = env->NewString((unsigned short*)urlString.characters(), urlString.length());
288     const WebCore::String& originalUrlString = item->originalURLString();
289     jstring originalUrlStr = NULL;
290     if (!originalUrlString.isNull()) {
291     	originalUrlStr = env->NewString(
292                 (unsigned short*) originalUrlString.characters(),
293                 originalUrlString.length());
294     }
295     const WebCore::String& titleString = item->title();
296     jstring titleStr = NULL;
297     if (!titleString.isNull())
298         titleStr = env->NewString((unsigned short*)titleString.characters(), titleString.length());
299 
300     // Try to get the favicon from the history item. For some pages like Grand
301     // Prix, there are history items with anchors. If the icon fails for the
302     // item, try to get the icon using the url without the ref.
303     jobject favicon = NULL;
304     WebCore::String url = item->urlString();
305     if (item->url().hasFragmentIdentifier()) {
306         int refIndex = url.reverseFind('#');
307         url = url.substring(0, refIndex);
308     }
309     WebCore::Image* icon = WebCore::iconDatabase()->iconForPageURL(url,
310             WebCore::IntSize(16, 16));
311 
312     if (icon)
313         favicon = webcoreImageToJavaBitmap(env, icon);
314 
315     WTF::Vector<char> data;
316     jbyteArray array = WebHistory::Flatten(env, data, item);
317     env->CallVoidMethod(realItem.get(), gWebHistoryItem.mUpdate, urlStr,
318             originalUrlStr, titleStr, favicon, array);
319     env->DeleteLocalRef(urlStr);
320     env->DeleteLocalRef(originalUrlStr);
321     env->DeleteLocalRef(titleStr);
322     if (favicon)
323         env->DeleteLocalRef(favicon);
324     env->DeleteLocalRef(array);
325 #endif
326 }
327 
historyItemChanged(WebCore::HistoryItem * item)328 static void historyItemChanged(WebCore::HistoryItem* item) {
329 #ifdef ANDROID_HISTORY_CLIENT
330     LOG_ASSERT(item,
331             "historyItemChanged called with a null item");
332     if (item->bridge())
333         item->bridge()->updateHistoryItem(item);
334 #endif
335 }
336 
AddItem(const AutoJObject & list,WebCore::HistoryItem * item)337 void WebHistory::AddItem(const AutoJObject& list, WebCore::HistoryItem* item)
338 {
339 #ifdef ANDROID_HISTORY_CLIENT
340     LOG_ASSERT(item, "newItem must take a valid HistoryItem!");
341     // Item already added. Should only happen when we are inflating the list.
342     if (item->bridge() || !list.get())
343         return;
344 
345     JNIEnv* env = list.env();
346     // Allocate a blank WebHistoryItem
347     jclass clazz = env->FindClass("android/webkit/WebHistoryItem");
348     jobject newItem = env->NewObject(clazz, gWebHistoryItem.mInit);
349 
350     // Create the bridge, make it active, and attach it to the item.
351     WebHistoryItem* bridge = new WebHistoryItem(env, newItem, item);
352     bridge->setActive();
353     item->setBridge(bridge);
354 
355     // Update the history item which will flatten the data and call update on
356     // the java item.
357     bridge->updateHistoryItem(item);
358 
359     // Add it to the list.
360     env->CallVoidMethod(list.get(), gWebBackForwardList.mAddHistoryItem, newItem);
361 
362     // Delete our local reference.
363     env->DeleteLocalRef(newItem);
364 #endif
365 }
366 
RemoveItem(const AutoJObject & list,int index)367 void WebHistory::RemoveItem(const AutoJObject& list, int index)
368 {
369     if (list.get())
370         list.env()->CallVoidMethod(list.get(), gWebBackForwardList.mRemoveHistoryItem, index);
371 }
372 
UpdateHistoryIndex(const AutoJObject & list,int newIndex)373 void WebHistory::UpdateHistoryIndex(const AutoJObject& list, int newIndex)
374 {
375     if (list.get())
376         list.env()->SetIntField(list.get(), gWebBackForwardList.mCurrentIndex, newIndex);
377 }
378 
write_string(WTF::Vector<char> & v,const WebCore::String & str)379 static void write_string(WTF::Vector<char>& v, const WebCore::String& str)
380 {
381     unsigned strLen = str.length();
382     // Only do work if the string has data.
383     if (strLen) {
384         // Determine how much to grow the vector. Use the worst case for utf8 to
385         // avoid reading the string twice. Add sizeof(unsigned) to hold the
386         // string length in utf8.
387         unsigned vectorLen = v.size() + sizeof(unsigned);
388         unsigned length = (strLen << 2) + vectorLen;
389         // Grow the vector. This will change the value of v.size() but we
390         // remember the original size above.
391         v.grow(length);
392         // Grab the position to write to.
393         char* data = v.begin() + vectorLen;
394         // Write the actual string
395         int l = SkUTF16_ToUTF8(str.characters(), strLen, data);
396         LOGV("Writing string       %d %.*s", l, l, data);
397         // Go back and write the utf8 length. Subtract sizeof(unsigned) from
398         // data to get the position to write the length.
399         memcpy(data - sizeof(unsigned), (char*)&l, sizeof(unsigned));
400         // Shrink the internal state of the vector so we match what was
401         // actually written.
402         v.shrink(vectorLen + l);
403     } else
404         v.append((char*)&strLen, sizeof(unsigned));
405 }
406 
write_item(WTF::Vector<char> & v,WebCore::HistoryItem * item)407 static void write_item(WTF::Vector<char>& v, WebCore::HistoryItem* item)
408 {
409     // Original url
410     write_string(v, item->originalURLString());
411 
412     // Url
413     write_string(v, item->urlString());
414 
415     // Title
416     write_string(v, item->title());
417 
418     // Form content type
419     write_string(v, item->formContentType());
420 
421     // Form data
422     const WebCore::FormData* formData = item->formData();
423     if (formData)
424         write_string(v, formData->flattenToString());
425     else
426         write_string(v, WebCore::String()); // Empty constructor does not allocate a buffer.
427 
428     // Target
429     write_string(v, item->target());
430 
431 #ifdef ANDROID_HISTORY_CLIENT
432     WebHistoryItem* bridge = item->bridge();
433     LOG_ASSERT(bridge, "We should have a bridge here!");
434     // Screen scale
435     int scale = bridge->scale();
436     LOGV("Writing scale %d", scale);
437     v.append((char*)&scale, sizeof(int));
438 #endif
439 
440     // Document state
441     const WTF::Vector<WebCore::String>& docState = item->documentState();
442     WTF::Vector<WebCore::String>::const_iterator end = docState.end();
443     unsigned stateSize = docState.size();
444     LOGV("Writing docState     %d", stateSize);
445     v.append((char*)&stateSize, sizeof(unsigned));
446     for (WTF::Vector<WebCore::String>::const_iterator i = docState.begin(); i != end; ++i) {
447         write_string(v, *i);
448     }
449 
450     // Is target item
451     LOGV("Writing isTargetItem %d", item->isTargetItem());
452     v.append((char)item->isTargetItem());
453 
454     // Children count
455     unsigned childCount = item->children().size();
456     LOGV("Writing childCount   %d", childCount);
457     v.append((char*)&childCount, sizeof(unsigned));
458 }
459 
write_children_recursive(WTF::Vector<char> & v,WebCore::HistoryItem * parent)460 static void write_children_recursive(WTF::Vector<char>& v, WebCore::HistoryItem* parent)
461 {
462     const WebCore::HistoryItemVector& children = parent->children();
463     WebCore::HistoryItemVector::const_iterator end = children.end();
464     for (WebCore::HistoryItemVector::const_iterator i = children.begin(); i != end; ++i) {
465         WebCore::HistoryItem* item = (*i).get();
466 #ifdef ANDROID_HISTORY_CLIENT
467         LOG_ASSERT(parent->bridge(),
468                 "The parent item should have a bridge object!");
469         if (!item->bridge()) {
470             WebHistoryItem* bridge = new WebHistoryItem(parent->bridge());
471             item->setBridge(bridge);
472             bridge->setActive();
473         } else {
474             // The only time this item's parent may not be the same as the
475             // parent's bridge is during history close. In that case, the
476             // parent must not have a parent bridge.
477             LOG_ASSERT(parent->bridge()->parent() == NULL ||
478                     item->bridge()->parent() == parent->bridge(),
479                     "Somehow this item has an incorrect parent");
480             item->bridge()->setParent(parent->bridge());
481         }
482 #endif
483         write_item(v, item);
484         write_children_recursive(v, item);
485     }
486 }
487 
read_item_recursive(WebCore::HistoryItem * newItem,const char ** pData,int length)488 static bool read_item_recursive(WebCore::HistoryItem* newItem,
489         const char** pData, int length)
490 {
491     if (!pData || length < HISTORY_MIN_SIZE)
492         return false;
493 
494     const WebCore::TextEncoding& e = WebCore::UTF8Encoding();
495     const char* data = *pData;
496     const char* end = data + length;
497     int sizeofUnsigned = (int)sizeof(unsigned);
498 
499     // Read the original url
500     // Read the expected length of the string.
501     int l;
502     memcpy(&l, data, sizeofUnsigned);
503     // Increment data pointer by the size of an unsigned int.
504     data += sizeofUnsigned;
505     if (l) {
506         LOGV("Original url    %d %.*s", l, l, data);
507         // If we have a length, check if that length exceeds the data length
508         // and return null if there is not enough data.
509         if (data + l < end)
510             newItem->setOriginalURLString(e.decode(data, l));
511         else
512             return false;
513         // Increment the data pointer by the length of the string.
514         data += l;
515     }
516     // Check if we have enough data left to continue.
517     if (end - data < sizeofUnsigned)
518         return false;
519 
520     // Read the url
521     memcpy(&l, data, sizeofUnsigned);
522     data += sizeofUnsigned;
523     if (l) {
524         LOGV("Url             %d %.*s", l, l, data);
525         if (data + l < end)
526             newItem->setURLString(e.decode(data, l));
527         else
528             return false;
529         data += l;
530     }
531     if (end - data < sizeofUnsigned)
532         return false;
533 
534     // Read the title
535     memcpy(&l, data, sizeofUnsigned);
536     data += sizeofUnsigned;
537     if (l) {
538         LOGV("Title           %d %.*s", l, l, data);
539         if (data + l < end)
540             newItem->setTitle(e.decode(data, l));
541         else
542             return false;
543         data += l;
544     }
545     if (end - data < sizeofUnsigned)
546         return false;
547 
548     // Generate a new ResourceRequest object for populating form information.
549     WebCore::String formContentType;
550     WTF::PassRefPtr<WebCore::FormData> formData = NULL;
551 
552     // Read the form content type
553     memcpy(&l, data, sizeofUnsigned);
554     data += sizeofUnsigned;
555     if (l) {
556         LOGV("Content type    %d %.*s", l, l, data);
557         if (data + l < end)
558             formContentType = e.decode(data, l);
559         else
560             return false;
561         data += l;
562     }
563     if (end - data < sizeofUnsigned)
564         return false;
565 
566     // Read the form data
567     memcpy(&l, data, sizeofUnsigned);
568     data += sizeofUnsigned;
569     if (l) {
570         LOGV("Form data       %d %.*s", l, l, data);
571         if (data + l < end)
572             formData = WebCore::FormData::create(data, l);
573         else
574             return false;
575         data += l;
576     }
577     if (end - data < sizeofUnsigned)
578         return false;
579 
580     // Set up the form info
581     if (formData != NULL) {
582         WebCore::ResourceRequest r;
583         r.setHTTPMethod("POST");
584         r.setHTTPContentType(formContentType);
585         r.setHTTPBody(formData);
586         newItem->setFormInfoFromRequest(r);
587     }
588 
589     // Read the target
590     memcpy(&l, data, sizeofUnsigned);
591     data += sizeofUnsigned;
592     if (l) {
593         LOGV("Target          %d %.*s", l, l, data);
594         if (data + l < end)
595             newItem->setTarget(e.decode(data, l));
596         else
597             return false;
598         data += l;
599     }
600     if (end - data < sizeofUnsigned)
601         return false;
602 
603 #ifdef ANDROID_HISTORY_CLIENT
604     WebHistoryItem* bridge = newItem->bridge();
605     LOG_ASSERT(bridge, "There should be a bridge object during inflate");
606     // Read the screen scale
607     memcpy(&l, data, sizeofUnsigned);
608     LOGV("Screen scale    %d", l);
609     bridge->setScale(l);
610     data += sizeofUnsigned;
611     if (end - data < sizeofUnsigned)
612         return false;
613 #endif
614 
615     // Read the document state
616     memcpy(&l, data, sizeofUnsigned);
617     LOGV("Document state  %d", l);
618     data += sizeofUnsigned;
619     if (l) {
620         // Check if we have enough data to at least parse the sizes of each
621         // document state string.
622         if (data + l * sizeofUnsigned >= end)
623             return false;
624         // Create a new vector and reserve enough space for the document state.
625         WTF::Vector<WebCore::String> docState;
626         docState.reserveCapacity(l);
627         while (l--) {
628             // Check each time if we have enough to parse the length of the next
629             // string.
630             if (end - data < sizeofUnsigned)
631                 return false;
632             int strLen;
633             memcpy(&strLen, data, sizeofUnsigned);
634             data += sizeofUnsigned;
635             if (data + strLen < end)
636                 docState.append(e.decode(data, strLen));
637             else
638                 return false;
639             LOGV("\t\t%d %.*s", strLen, strLen, data);
640             data += strLen;
641         }
642         newItem->setDocumentState(docState);
643     }
644     // Check if we have enough to read the next byte
645     if (data >= end)
646         return false;
647 
648     // Read is target item
649     // Cast the value to unsigned char in order to make a negative value larger
650     // than 1. A value that is not 0 or 1 is a failure.
651     unsigned char c = (unsigned char)data[0];
652     if (c > 1)
653         return false;
654     LOGV("Target item     %d", c);
655     newItem->setIsTargetItem((bool)c);
656     data++;
657     if (end - data < sizeofUnsigned)
658         return false;
659 
660     // Read the child count
661     memcpy(&l, data, sizeofUnsigned);
662     LOGV("Child count     %d", l);
663     data += sizeofUnsigned;
664     *pData = data;
665     if (l) {
666         // Check if we have the minimum amount need to parse l children.
667         if (data + l * HISTORY_MIN_SIZE >= end)
668             return false;
669         while (l--) {
670             // No need to check the length each time because read_item_recursive
671             // will return null if there isn't enough data left to parse.
672             WTF::PassRefPtr<WebCore::HistoryItem> child = WebCore::HistoryItem::create();
673 #ifdef ANDROID_HISTORY_CLIENT
674             // Set a bridge that will not call into java.
675             child->setBridge(new WebHistoryItem(bridge));
676 #endif
677             // Read the child item.
678             if (!read_item_recursive(child.get(), pData, end - data)) {
679                 child.clear();
680                 return false;
681             }
682 #ifdef ANDROID_HISTORY_CLIENT
683             child->bridge()->setActive();
684 #endif
685             newItem->addChildItem(child);
686         }
687     }
688     return true;
689 }
690 
691 // On arm, this test will cause memory corruption since converting char* will
692 // byte align the result and this test does not use memset (it probably
693 // should).
694 // On the simulator, using HistoryItem will invoke the IconDatabase which will
695 // initialize the main thread. Since this is invoked by the Zygote process, the
696 // main thread will be incorrect and an assert will fire later.
697 // In conclusion, define UNIT_TEST only if you know what you are doing.
698 #ifdef UNIT_TEST
unit_test()699 static void unit_test()
700 {
701     LOGD("Entering history unit test!");
702     const char* test1 = new char[0];
703     WTF::RefPtr<WebCore::HistoryItem> item = WebCore::HistoryItem::create();
704     WebCore::HistoryItem* testItem = item.get();
705 #ifdef ANDROID_HISTORY_CLIENT
706     testItem->setBridge(new WebHistoryItem(NULL));
707 #endif
708     LOG_ASSERT(!read_item_recursive(testItem, &test1, 0), "0 length array should fail!");
709     delete[] test1;
710     const char* test2 = new char[2];
711     LOG_ASSERT(!read_item_recursive(testItem, &test2, 2), "Small array should fail!");
712     delete[] test2;
713     LOG_ASSERT(!read_item_recursive(testItem, NULL, HISTORY_MIN_SIZE), "Null data should fail!");
714     // Original Url
715     char* test3 = new char[HISTORY_MIN_SIZE];
716     const char* ptr = (const char*)test3;
717     memset(test3, 0, HISTORY_MIN_SIZE);
718     *(int*)test3 = 4000;
719     LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length originalUrl should fail!");
720     // Url
721     int offset = 4;
722     memset(test3, 0, HISTORY_MIN_SIZE);
723     ptr = (const char*)test3;
724     *(int*)(test3 + offset) = 4000;
725     LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length url should fail!");
726     // Title
727     offset += 4;
728     memset(test3, 0, HISTORY_MIN_SIZE);
729     ptr = (const char*)test3;
730     *(int*)(test3 + offset) = 4000;
731     LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length title should fail!");
732     // Form content type
733     offset += 4;
734     memset(test3, 0, HISTORY_MIN_SIZE);
735     ptr = (const char*)test3;
736     *(int*)(test3 + offset) = 4000;
737     LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length contentType should fail!");
738     // Form data
739     offset += 4;
740     memset(test3, 0, HISTORY_MIN_SIZE);
741     ptr = (const char*)test3;
742     *(int*)(test3 + offset) = 4000;
743     LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length form data should fail!");
744     // Target
745     offset += 4;
746     memset(test3, 0, HISTORY_MIN_SIZE);
747     ptr = (const char*)test3;
748     *(int*)(test3 + offset) = 4000;
749     LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length target should fail!");
750 #ifdef ANDROID_HISTORY_CLIENT
751     offset += 4; // Scale
752 #endif
753     // Document state
754     offset += 4;
755     memset(test3, 0, HISTORY_MIN_SIZE);
756     ptr = (const char*)test3;
757     *(int*)(test3 + offset) = 4000;
758     LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length document state should fail!");
759     // Is target item
760     offset += 1;
761     memset(test3, 0, HISTORY_MIN_SIZE);
762     ptr = (const char*)test3;
763     *(char*)(test3 + offset) = '!';
764     LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "IsTargetItem should fail with ! as the value!");
765     // Child count
766     offset += 4;
767     memset(test3, 0, HISTORY_MIN_SIZE);
768     ptr = (const char*)test3;
769     *(int*)(test3 + offset) = 4000;
770     LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 kids should fail!");
771 
772 #ifdef ANDROID_HISTORY_CLIENT
773     offset = 36;
774 #else
775     offset = 28;
776 #endif
777     // Test document state
778     delete[] test3;
779     test3 = new char[HISTORY_MIN_SIZE + sizeof(unsigned)];
780     memset(test3, 0, HISTORY_MIN_SIZE + sizeof(unsigned));
781     ptr = (const char*)test3;
782     *(int*)(test3 + offset) = 1;
783     *(int*)(test3 + offset + 4) = 20;
784     LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE + sizeof(unsigned)), "1 20 length document state string should fail!");
785     delete[] test3;
786     test3 = new char[HISTORY_MIN_SIZE + 2 * sizeof(unsigned)];
787     memset(test3, 0, HISTORY_MIN_SIZE + 2 * sizeof(unsigned));
788     ptr = (const char*)test3;
789     *(int*)(test3 + offset) = 2;
790     *(int*)(test3 + offset + 4) = 0;
791     *(int*)(test3 + offset + 8) = 20;
792     LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE + 2 * sizeof(unsigned) ), "2 20 length document state string should fail!");
793     delete[] test3;
794 }
795 #endif
796 
797 //---------------------------------------------------------
798 // JNI registration
799 //---------------------------------------------------------
800 static JNINativeMethod gWebBackForwardListMethods[] = {
801     { "nativeClose", "(I)V",
802         (void*) WebHistoryClose },
803     { "restoreIndex", "(II)V",
804         (void*) WebHistoryRestoreIndex }
805 };
806 
807 static JNINativeMethod gWebHistoryItemMethods[] = {
808     { "inflate", "(I[B)V",
809         (void*) WebHistoryInflate }
810 };
811 
register_webhistory(JNIEnv * env)812 int register_webhistory(JNIEnv* env)
813 {
814 #ifdef ANDROID_HISTORY_CLIENT
815     // Get notified of all changes to history items.
816     WebCore::notifyHistoryItemChanged = historyItemChanged;
817 #endif
818 #ifdef UNIT_TEST
819     unit_test();
820 #endif
821     // Find WebHistoryItem, its constructor, and the update method.
822     jclass clazz = env->FindClass("android/webkit/WebHistoryItem");
823     LOG_ASSERT(clazz, "Unable to find class android/webkit/WebHistoryItem");
824     gWebHistoryItem.mInit = env->GetMethodID(clazz, "<init>", "()V");
825     LOG_ASSERT(gWebHistoryItem.mInit, "Could not find WebHistoryItem constructor");
826     gWebHistoryItem.mUpdate = env->GetMethodID(clazz, "update",
827             "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/graphics/Bitmap;[B)V");
828     LOG_ASSERT(gWebHistoryItem.mUpdate, "Could not find method update in WebHistoryItem");
829 
830     // Find the field ids for mTitle and mUrl.
831     gWebHistoryItem.mTitle = env->GetFieldID(clazz, "mTitle", "Ljava/lang/String;");
832     LOG_ASSERT(gWebHistoryItem.mTitle, "Could not find field mTitle in WebHistoryItem");
833     gWebHistoryItem.mUrl = env->GetFieldID(clazz, "mUrl", "Ljava/lang/String;");
834     LOG_ASSERT(gWebHistoryItem.mUrl, "Could not find field mUrl in WebHistoryItem");
835 
836     // Find the WebBackForwardList object, the addHistoryItem and
837     // removeHistoryItem methods and the mCurrentIndex field.
838     clazz = env->FindClass("android/webkit/WebBackForwardList");
839     LOG_ASSERT(clazz, "Unable to find class android/webkit/WebBackForwardList");
840     gWebBackForwardList.mAddHistoryItem = env->GetMethodID(clazz, "addHistoryItem",
841             "(Landroid/webkit/WebHistoryItem;)V");
842     LOG_ASSERT(gWebBackForwardList.mAddHistoryItem, "Could not find method addHistoryItem");
843     gWebBackForwardList.mRemoveHistoryItem = env->GetMethodID(clazz, "removeHistoryItem",
844             "(I)V");
845     LOG_ASSERT(gWebBackForwardList.mRemoveHistoryItem, "Could not find method removeHistoryItem");
846     gWebBackForwardList.mCurrentIndex = env->GetFieldID(clazz, "mCurrentIndex", "I");
847     LOG_ASSERT(gWebBackForwardList.mCurrentIndex, "Could not find field mCurrentIndex");
848 
849     int result = jniRegisterNativeMethods(env, "android/webkit/WebBackForwardList",
850             gWebBackForwardListMethods, NELEM(gWebBackForwardListMethods));
851     return (result < 0) ? result : jniRegisterNativeMethods(env, "android/webkit/WebHistoryItem",
852             gWebHistoryItemMethods, NELEM(gWebHistoryItemMethods));
853 }
854 
855 } /* namespace android */
856