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 WebHistoryItems
64 struct WebHistoryItemFields {
65 jmethodID mInit;
66 } gWebHistoryItem;
67
68 struct WebBackForwardListFields {
69 jmethodID mAddHistoryItem;
70 jmethodID mRemoveHistoryItem;
71 jmethodID mSetCurrentIndex;
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 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 WebHistoryItem
402 jclass clazz = env->FindClass("android/webkit/WebHistoryItem");
403 jobject newItem = env->NewObject(clazz, gWebHistoryItem.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(), gWebBackForwardList.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(), gWebBackForwardList.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(), gWebBackForwardList.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 writeString(vector, formData->flattenToString());
476 // save the identifier as it is not included in the flatten data
477 int64_t id = formData->identifier();
478 vector.append((char*)&id, sizeof(int64_t));
479 } else
480 writeString(vector, WTF::String()); // Empty constructor does not allocate a buffer.
481
482 // Target
483 writeString(vector, item->target());
484
485 AndroidWebHistoryBridge* bridge = item->bridge();
486 ALOG_ASSERT(bridge, "We should have a bridge here!");
487 // Screen scale
488 const float scale = bridge->scale();
489 ALOGV("Writing scale %f", scale);
490 vector.append((char*)&scale, sizeof(float));
491 const float textWrapScale = bridge->textWrapScale();
492 ALOGV("Writing text wrap scale %f", textWrapScale);
493 vector.append((char*)&textWrapScale, sizeof(float));
494
495 // Scroll position.
496 const int scrollX = item->scrollPoint().x();
497 vector.append((char*)&scrollX, sizeof(int));
498 const int scrollY = item->scrollPoint().y();
499 vector.append((char*)&scrollY, sizeof(int));
500
501 // Document state
502 const WTF::Vector<WTF::String>& docState = item->documentState();
503 WTF::Vector<WTF::String>::const_iterator end = docState.end();
504 unsigned stateSize = docState.size();
505 ALOGV("Writing docState %d", stateSize);
506 vector.append((char*)&stateSize, sizeof(unsigned));
507 for (WTF::Vector<WTF::String>::const_iterator i = docState.begin(); i != end; ++i) {
508 writeString(vector, *i);
509 }
510
511 // Is target item
512 ALOGV("Writing isTargetItem %d", item->isTargetItem());
513 vector.append((char)item->isTargetItem());
514
515 // Children count
516 unsigned childCount = item->children().size();
517 ALOGV("Writing childCount %d", childCount);
518 vector.append((char*)&childCount, sizeof(unsigned));
519 }
520
writeChildrenRecursive(WTF::Vector<char> & vector,WebCore::HistoryItem * parent)521 static void writeChildrenRecursive(WTF::Vector<char>& vector, WebCore::HistoryItem* parent)
522 {
523 const WebCore::HistoryItemVector& children = parent->children();
524 WebCore::HistoryItemVector::const_iterator end = children.end();
525 for (WebCore::HistoryItemVector::const_iterator i = children.begin(); i != end; ++i) {
526 WebCore::HistoryItem* item = (*i).get();
527 ALOG_ASSERT(parent->bridge(),
528 "The parent item should have a bridge object!");
529 if (!item->bridge()) {
530 WebHistoryItem* bridge = new WebHistoryItem(static_cast<WebHistoryItem*>(parent->bridge()));
531 item->setBridge(bridge);
532 bridge->setActive();
533 } else {
534 // The only time this item's parent may not be the same as the
535 // parent's bridge is during history close. In that case, the
536 // parent must not have a parent bridge.
537 WebHistoryItem* bridge = static_cast<WebHistoryItem*>(item->bridge());
538 WebHistoryItem* parentBridge = static_cast<WebHistoryItem*>(parent->bridge());
539 ALOG_ASSERT(parentBridge->parent() == 0 ||
540 bridge->parent() == parentBridge,
541 "Somehow this item has an incorrect parent");
542 bridge->setParent(parentBridge);
543 }
544 writeItem(vector, item);
545 writeChildrenRecursive(vector, item);
546 }
547 }
548
549 bool readUnsigned(const char*& data, const char* end, unsigned& result, const char* dbgLabel = 0);
550 bool readInt(const char*& data, const char* end, int& result, const char* dbgLabel = 0);
551 bool readInt64(const char*& data, const char* end, int64_t& result, const char* dbgLabel = 0);
552 bool readFloat(const char*& data, const char* end, float& result, const char* dbgLabel = 0);
553 bool readBool(const char*& data, const char* end, bool& result, const char* dbgLabel = 0);
554 bool readString(const char*& data, const char* end, String& result, const char* dbgLabel = 0);
555
readUnsigned(const char * & data,const char * end,unsigned & result,const char * dbgLabel)556 bool readUnsigned(const char*& data, const char* end, unsigned& result, const char* dbgLabel)
557 {
558 // Check if we have enough data left to continue.
559 if ((end < data) || (static_cast<size_t>(end - data) < sizeof(unsigned))) {
560 ALOGW("\tNot enough data to read unsigned; tag=\"%s\" end=%p data=%p",
561 dbgLabel ? dbgLabel : "<no tag>", end, data);
562 return false;
563 }
564
565 memcpy(&result, data, sizeof(unsigned));
566 data += sizeof(unsigned);
567 if (dbgLabel)
568 ALOGV("Reading %-16s %u", dbgLabel, result);
569 return true;
570 }
571
readInt(const char * & data,const char * end,int & result,const char * dbgLabel)572 bool readInt(const char*& data, const char* end, int& result, const char* dbgLabel)
573 {
574 // Check if we have enough data left to continue.
575 if ((end < data) || (static_cast<size_t>(end - data) < sizeof(int))) {
576 ALOGW("Not enough data to read int; tag=\"%s\" end=%p data=%p",
577 dbgLabel ? dbgLabel : "<no tag>", end, data);
578 return false;
579 }
580
581 memcpy(&result, data, sizeof(int));
582 data += sizeof(int);
583 if (dbgLabel)
584 ALOGV("Reading %-16s %d", dbgLabel, result);
585 return true;
586 }
587
readInt64(const char * & data,const char * end,int64_t & result,const char * dbgLabel)588 bool readInt64(const char*& data, const char* end, int64_t& result, const char* dbgLabel)
589 {
590 // Check if we have enough data left to continue.
591 if ((end < data) || (static_cast<size_t>(end - data) < sizeof(int64_t))) {
592 ALOGW("Not enough data to read int64_t; tag=\"%s\" end=%p data=%p",
593 dbgLabel ? dbgLabel : "<no tag>", end, data);
594 return false;
595 }
596
597 memcpy(&result, data, sizeof(int64_t));
598 data += sizeof(int64_t);
599 if (dbgLabel)
600 ALOGV("Reading %-16s %ll", dbgLabel, result);
601 return true;
602 }
603
readFloat(const char * & data,const char * end,float & result,const char * dbgLabel)604 bool readFloat(const char*& data, const char* end, float& result, const char* dbgLabel)
605 {
606 // Check if we have enough data left to continue.
607 if ((end < data) || (static_cast<size_t>(end - data) < sizeof(float))) {
608 ALOGW("Not enough data to read float; tag=\"%s\" end=%p data=%p",
609 dbgLabel ? dbgLabel : "<no tag>", end, data);
610 return false;
611 }
612
613 memcpy(&result, data, sizeof(float));
614 data += sizeof(float);
615 if (dbgLabel)
616 ALOGV("Reading %-16s %f", dbgLabel, result);
617 return true;
618 }
619
620 // Note that the return value indicates success or failure, while the result
621 // parameter indicates the read value of the bool
readBool(const char * & data,const char * end,bool & result,const char * dbgLabel)622 bool readBool(const char*& data, const char* end, bool& result, const char* dbgLabel)
623 {
624 // Check if we have enough data left to continue.
625 if ((end < data) || (static_cast<size_t>(end - data) < sizeof(char))) {
626 ALOGW("Not enough data to read bool; tag=\"%s\" end=%p data=%p",
627 dbgLabel ? dbgLabel : "<no tag>", end, data);
628 return false;
629 }
630
631 char c;
632 memcpy(&c, data, sizeof(char));
633 data += sizeof(char);
634 if (dbgLabel)
635 ALOGV("Reading %-16s %d", dbgLabel, c);
636 result = c;
637
638 // Valid bool results are 0 or 1
639 if ((c != 0) && (c != 1)) {
640 ALOGW("Invalid value for bool; tag=\"%s\" end=%p data=%p c=%u",
641 dbgLabel ? dbgLabel : "<no tag>", end, data, c);
642 return false;
643 }
644
645 return true;
646 }
647
readString(const char * & data,const char * end,String & result,const char * dbgLabel)648 bool readString(const char*& data, const char* end, String& result, const char* dbgLabel)
649 {
650 unsigned stringLength;
651 if (!readUnsigned(data, end, stringLength)) {
652 ALOGW("Not enough data to read string length; tag=\"%s\" end=%p data=%p",
653 dbgLabel ? dbgLabel : "<no tag>", end, data);
654 return false;
655 }
656
657 if (dbgLabel)
658 ALOGV("Reading %-16s %d %.*s", dbgLabel, stringLength, stringLength, data);
659
660 // If length was 0, there will be no string content, but still return true
661 if (!stringLength) {
662 result = String();
663 return true;
664 }
665
666 if ((end < data) || ((unsigned)(end - data) < stringLength)) {
667 ALOGW("Not enough data to read content; tag=\"%s\" end=%p data=%p stringLength=%u",
668 dbgLabel ? dbgLabel : "<no tag>", end, data, stringLength);
669 return false;
670 }
671
672 const unsigned MAX_REASONABLE_STRING_LENGTH = 10000;
673 if (stringLength > MAX_REASONABLE_STRING_LENGTH) {
674 ALOGW("String length is suspiciously large (>%d); tag=\"%s\" end=%p data=%p stringLength=%u",
675 MAX_REASONABLE_STRING_LENGTH, dbgLabel ? dbgLabel : "<no tag>",
676 end, data, stringLength);
677 }
678
679 bool decodeFailed = false;
680 static const WebCore::TextEncoding& encoding = WebCore::UTF8Encoding();
681 result = encoding.decode(data, stringLength, true, decodeFailed);
682 if (decodeFailed) {
683 ALOGW("Decode failed, tag=\"%s\" end=%p data=%p stringLength=%u content=\"%s\"",
684 dbgLabel ? dbgLabel : "<no tag>", end, data, stringLength,
685 result.utf8().data());
686 return false;
687 }
688
689 if (stringLength > MAX_REASONABLE_STRING_LENGTH) {
690 ALOGW("\tdecodeFailed=%d (flag is ignored) content=\"%s\"",
691 decodeFailed, result.utf8().data());
692 }
693
694 data += stringLength;
695 return true;
696 }
697
readItemRecursive(WebCore::HistoryItem * newItem,const char ** pData,int length)698 static bool readItemRecursive(WebCore::HistoryItem* newItem,
699 const char** pData, int length)
700 {
701 if (!pData || length < HISTORY_MIN_SIZE) {
702 ALOGW("readItemRecursive() bad params; pData=%p length=%d", pData, length);
703 return false;
704 }
705
706 const char* data = *pData;
707 const char* end = data + length;
708 String content;
709
710 // Read the original url
711 if (readString(data, end, content, "Original url"))
712 newItem->setOriginalURLString(content);
713 else
714 return false;
715
716 // Read the url
717 if (readString(data, end, content, "Url"))
718 newItem->setURLString(content);
719 else
720 return false;
721
722 // Read the title
723 if (readString(data, end, content, "Title"))
724 newItem->setTitle(content);
725 else
726 return false;
727
728 // Generate a new ResourceRequest object for populating form information.
729 // Read the form content type
730 WTF::String formContentType;
731 if (!readString(data, end, formContentType, "Content type"))
732 return false;
733
734 // Read the form data size
735 unsigned formDataSize;
736 if (!readUnsigned(data, end, formDataSize, "Form data size"))
737 return false;
738
739 // Read the form data
740 WTF::RefPtr<WebCore::FormData> formData;
741 if (formDataSize) {
742 ALOGV("Reading Form data %d %.*s", formDataSize, formDataSize, data);
743 if ((end < data) || ((size_t)(end - data) < formDataSize)) {
744 ALOGW("\tNot enough data to read form data; returning");
745 return false;
746 }
747 formData = WebCore::FormData::create(data, formDataSize);
748 data += formDataSize;
749 // Read the identifier
750 int64_t id;
751 if (!readInt64(data, end, id, "Form id"))
752 return false;
753 if (id)
754 formData->setIdentifier(id);
755 }
756
757 // Set up the form info
758 if (formData != NULL) {
759 WebCore::ResourceRequest r;
760 r.setHTTPMethod("POST");
761 r.setHTTPContentType(formContentType);
762 r.setHTTPBody(formData);
763 newItem->setFormInfoFromRequest(r);
764 }
765
766 // Read the target
767 if (readString(data, end, content, "Target"))
768 newItem->setTarget(content);
769 else
770 return false;
771
772 AndroidWebHistoryBridge* bridge = newItem->bridge();
773 ALOG_ASSERT(bridge, "There should be a bridge object during inflate");
774
775 // Read the screen scale
776 float fValue;
777 if (readFloat(data, end, fValue, "Screen scale"))
778 bridge->setScale(fValue);
779 else
780 return false;
781
782 // Read the text wrap scale
783 if (readFloat(data, end, fValue, "Text wrap scale"))
784 bridge->setTextWrapScale(fValue);
785 else
786 return false;
787
788 // Read scroll position.
789 int scrollX;
790 if (!readInt(data, end, scrollX, "Scroll pos x"))
791 return false;
792 int scrollY;
793 if (!readInt(data, end, scrollY, "Scroll pos y"))
794 return false;
795 newItem->setScrollPoint(IntPoint(scrollX, scrollY));
796
797 // Read the document state
798 unsigned docStateCount;
799 if (!readUnsigned(data, end, docStateCount, "Doc state count"))
800 return false;
801 if (docStateCount) {
802 // Create a new vector and reserve enough space for the document state.
803 WTF::Vector<WTF::String> docState;
804 docState.reserveCapacity(docStateCount);
805 while (docStateCount--) {
806 // Read a document state string
807 if (readString(data, end, content, "Document state"))
808 docState.append(content);
809 else
810 return false;
811 }
812 newItem->setDocumentState(docState);
813 }
814
815 // Read is target item
816 bool c;
817 if (readBool(data, end, c, "Target item"))
818 newItem->setIsTargetItem(c);
819 else
820 return false;
821
822 // Read the child count
823 unsigned count;
824 if (!readUnsigned(data, end, count, "Child count"))
825 return false;
826 *pData = data;
827 if (count) {
828 while (count--) {
829 // No need to check the length each time because read_item_recursive
830 // will return null if there isn't enough data left to parse.
831 WTF::RefPtr<WebCore::HistoryItem> child = WebCore::HistoryItem::create();
832 // Set a bridge that will not call into java.
833 child->setBridge(new WebHistoryItem(static_cast<WebHistoryItem*>(bridge)));
834 // Read the child item.
835 if (!readItemRecursive(child.get(), pData, end - data))
836 return false;
837 child->bridge()->setActive();
838 newItem->addChildItem(child);
839 }
840 }
841 return true;
842 }
843
844 // On arm, this test will cause memory corruption since converting char* will
845 // byte align the result and this test does not use memset (it probably
846 // should).
847 // On the simulator, using HistoryItem will invoke the IconDatabase which will
848 // initialize the main thread. Since this is invoked by the Zygote process, the
849 // main thread will be incorrect and an assert will fire later.
850 // In conclusion, define UNIT_TEST only if you know what you are doing.
851 #ifdef UNIT_TEST
unitTest()852 static void unitTest()
853 {
854 ALOGD("Entering history unit test!");
855 const char* test1 = new char[0];
856 WTF::RefPtr<WebCore::HistoryItem> item = WebCore::HistoryItem::create();
857 WebCore::HistoryItem* testItem = item.get();
858 testItem->setBridge(new WebHistoryItem(0));
859 ALOG_ASSERT(!readItemRecursive(testItem, &test1, 0), "0 length array should fail!");
860 delete[] test1;
861 const char* test2 = new char[2];
862 ALOG_ASSERT(!readItemRecursive(testItem, &test2, 2), "Small array should fail!");
863 delete[] test2;
864 ALOG_ASSERT(!readItemRecursive(testItem, NULL, HISTORY_MIN_SIZE), "Null data should fail!");
865 // Original Url
866 char* test3 = new char[HISTORY_MIN_SIZE];
867 const char* ptr = (const char*)test3;
868 memset(test3, 0, HISTORY_MIN_SIZE);
869 *(int*)test3 = 4000;
870 ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length originalUrl should fail!");
871 // Url
872 int offset = 4;
873 memset(test3, 0, HISTORY_MIN_SIZE);
874 ptr = (const char*)test3;
875 *(int*)(test3 + offset) = 4000;
876 ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length url should fail!");
877 // Title
878 offset += 4;
879 memset(test3, 0, HISTORY_MIN_SIZE);
880 ptr = (const char*)test3;
881 *(int*)(test3 + offset) = 4000;
882 ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length title should fail!");
883 // Form content type
884 offset += 4;
885 memset(test3, 0, HISTORY_MIN_SIZE);
886 ptr = (const char*)test3;
887 *(int*)(test3 + offset) = 4000;
888 ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length contentType should fail!");
889 // Form data
890 offset += 4;
891 memset(test3, 0, HISTORY_MIN_SIZE);
892 ptr = (const char*)test3;
893 *(int*)(test3 + offset) = 4000;
894 ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length form data should fail!");
895 // Target
896 offset += 4;
897 memset(test3, 0, HISTORY_MIN_SIZE);
898 ptr = (const char*)test3;
899 *(int*)(test3 + offset) = 4000;
900 ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length target should fail!");
901 offset += 4; // Screen scale
902 offset += 4; // Text wrap scale
903 offset += 4; // Scroll pos x
904 offset += 4; // Scroll pos y
905 // Document state
906 offset += 4;
907 memset(test3, 0, HISTORY_MIN_SIZE);
908 ptr = (const char*)test3;
909 *(int*)(test3 + offset) = 4000;
910 ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length document state should fail!");
911 // Is target item
912 offset += 1;
913 memset(test3, 0, HISTORY_MIN_SIZE);
914 ptr = (const char*)test3;
915 *(char*)(test3 + offset) = '!';
916 ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "IsTargetItem should fail with ! as the value!");
917 // Child count
918 offset += 4;
919 memset(test3, 0, HISTORY_MIN_SIZE);
920 ptr = (const char*)test3;
921 *(int*)(test3 + offset) = 4000;
922 ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 kids should fail!");
923 // Test document state
924 offset = 40;
925 delete[] test3;
926 test3 = new char[HISTORY_MIN_SIZE + sizeof(unsigned)];
927 memset(test3, 0, HISTORY_MIN_SIZE + sizeof(unsigned));
928 ptr = (const char*)test3;
929 *(int*)(test3 + offset) = 1;
930 *(int*)(test3 + offset + 4) = 20;
931 ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE + sizeof(unsigned)), "1 20 length document state string should fail!");
932 delete[] test3;
933 test3 = new char[HISTORY_MIN_SIZE + 2 * sizeof(unsigned)];
934 memset(test3, 0, HISTORY_MIN_SIZE + 2 * sizeof(unsigned));
935 ptr = (const char*)test3;
936 *(int*)(test3 + offset) = 2;
937 *(int*)(test3 + offset + 4) = 0;
938 *(int*)(test3 + offset + 8) = 20;
939 ALOG_ASSERT(!readItemRecursive(testItem, &ptr, HISTORY_MIN_SIZE + 2 * sizeof(unsigned) ), "2 20 length document state string should fail!");
940 delete[] test3;
941 ALOGD("Leaving history unit test!");
942 }
943 #endif
944
945 //---------------------------------------------------------
946 // JNI registration
947 //---------------------------------------------------------
948 static JNINativeMethod gWebBackForwardListMethods[] = {
949 { "nativeClose", "(I)V",
950 (void*) WebHistoryClose },
951 { "restoreIndex", "(II)V",
952 (void*) WebHistoryRestoreIndex }
953 };
954
955 static JNINativeMethod gWebHistoryItemMethods[] = {
956 { "inflate", "(I[B)I",
957 (void*) WebHistoryInflate },
958 { "nativeRef", "(I)V",
959 (void*) WebHistoryRef },
960 { "nativeUnref", "(I)V",
961 (void*) WebHistoryUnref },
962 { "nativeGetTitle", "(I)Ljava/lang/String;",
963 (void*) WebHistoryGetTitle },
964 { "nativeGetUrl", "(I)Ljava/lang/String;",
965 (void*) WebHistoryGetUrl },
966 { "nativeGetOriginalUrl", "(I)Ljava/lang/String;",
967 (void*) WebHistoryGetOriginalUrl },
968 { "nativeGetFlattenedData", "(I)[B",
969 (void*) WebHistoryGetFlattenedData },
970 { "nativeGetFavicon", "(I)Landroid/graphics/Bitmap;",
971 (void*) WebHistoryGetFavicon },
972 };
973
registerWebHistory(JNIEnv * env)974 int registerWebHistory(JNIEnv* env)
975 {
976 // Get notified of all changes to history items.
977 WebCore::notifyHistoryItemChanged = historyItemChanged;
978 #ifdef UNIT_TEST
979 unitTest();
980 #endif
981 // Find WebHistoryItem, its constructor, and the update method.
982 jclass clazz = env->FindClass("android/webkit/WebHistoryItem");
983 ALOG_ASSERT(clazz, "Unable to find class android/webkit/WebHistoryItem");
984 gWebHistoryItem.mInit = env->GetMethodID(clazz, "<init>", "(I)V");
985 ALOG_ASSERT(gWebHistoryItem.mInit, "Could not find WebHistoryItem constructor");
986
987 env->DeleteLocalRef(clazz);
988
989 // Find the WebBackForwardList object and method.
990 clazz = env->FindClass("android/webkit/WebBackForwardList");
991 ALOG_ASSERT(clazz, "Unable to find class android/webkit/WebBackForwardList");
992 gWebBackForwardList.mAddHistoryItem = env->GetMethodID(clazz, "addHistoryItem",
993 "(Landroid/webkit/WebHistoryItem;)V");
994 ALOG_ASSERT(gWebBackForwardList.mAddHistoryItem, "Could not find method addHistoryItem");
995 gWebBackForwardList.mRemoveHistoryItem = env->GetMethodID(clazz, "removeHistoryItem",
996 "(I)V");
997 ALOG_ASSERT(gWebBackForwardList.mRemoveHistoryItem, "Could not find method removeHistoryItem");
998 gWebBackForwardList.mSetCurrentIndex = env->GetMethodID(clazz, "setCurrentIndex", "(I)V");
999 ALOG_ASSERT(gWebBackForwardList.mSetCurrentIndex, "Could not find method setCurrentIndex");
1000 env->DeleteLocalRef(clazz);
1001
1002 int result = jniRegisterNativeMethods(env, "android/webkit/WebBackForwardList",
1003 gWebBackForwardListMethods, NELEM(gWebBackForwardListMethods));
1004 return (result < 0) ? result : jniRegisterNativeMethods(env, "android/webkit/WebHistoryItem",
1005 gWebHistoryItemMethods, NELEM(gWebHistoryItemMethods));
1006 }
1007
1008 } /* namespace android */
1009