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