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