• 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 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 
WebHistoryClose(JNIEnv * env,jobject obj,jint frame)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 
WebHistoryRestoreIndex(JNIEnv * env,jobject obj,jint frame,jint index)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 
WebHistoryInflate(JNIEnv * env,jobject obj,jint frame,jbyteArray data)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 
WebHistoryRef(JNIEnv * env,jobject obj,jint ptr)198 static void WebHistoryRef(JNIEnv* env, jobject obj, jint ptr)
199 {
200     if (ptr)
201         reinterpret_cast<WebHistoryItem*>(ptr)->ref();
202 }
203 
WebHistoryUnref(JNIEnv * env,jobject obj,jint ptr)204 static void WebHistoryUnref(JNIEnv* env, jobject obj, jint ptr)
205 {
206     if (ptr)
207         reinterpret_cast<WebHistoryItem*>(ptr)->deref();
208 }
209 
WebHistoryGetTitle(JNIEnv * env,jobject obj,jint ptr)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 
WebHistoryGetUrl(JNIEnv * env,jobject obj,jint ptr)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 
WebHistoryGetOriginalUrl(JNIEnv * env,jobject obj,jint ptr)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 
WebHistoryGetFlattenedData(JNIEnv * env,jobject obj,jint ptr)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 
WebHistoryGetFavicon(JNIEnv * env,jobject obj,jint ptr)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 
Flatten(JNIEnv * env,WTF::Vector<char> & vector,WebCore::HistoryItem * item)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 
updateHistoryItem(WebCore::HistoryItem * item)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 
~WebHistoryItem()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 
historyItemChanged(WebCore::HistoryItem * item)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 
AddItem(const AutoJObject & list,WebCore::HistoryItem * item)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 
RemoveItem(const AutoJObject & list,int index)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 
UpdateHistoryIndex(const AutoJObject & list,int newIndex)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 
writeString(WTF::Vector<char> & vector,const WTF::String & str)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 
writeItem(WTF::Vector<char> & vector,WebCore::HistoryItem * item)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 
writeChildrenRecursive(WTF::Vector<char> & vector,WebCore::HistoryItem * parent)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 
readUnsigned(const char * & data,const char * end,unsigned & result,const char * dbgLabel)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 
readInt(const char * & data,const char * end,int & result,const char * dbgLabel)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 
readInt64(const char * & data,const char * end,int64_t & result,const char * dbgLabel)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 
readFloat(const char * & data,const char * end,float & result,const char * dbgLabel)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
readBool(const char * & data,const char * end,bool & result,const char * dbgLabel)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 
readString(const char * & data,const char * end,String & result,const char * dbgLabel)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 
readItemRecursive(WebCore::HistoryItem * newItem,const char ** pData,int length)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
unitTest()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 
registerWebHistory(JNIEnv * env)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