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 "BackForwardListImpl.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 #include <wtf/text/CString.h> 53 54 namespace android { 55 56 // Forward declarations 57 static void write_item(WTF::Vector<char>& v, WebCore::HistoryItem* item); 58 static void write_children_recursive(WTF::Vector<char>& v, WebCore::HistoryItem* parent); 59 static bool read_item_recursive(WebCore::HistoryItem* child, const char** pData, int length); 60 61 // Field ids for WebHistoryItems 62 struct WebHistoryItemFields { 63 jmethodID mInit; 64 jmethodID mUpdate; 65 jfieldID mTitle; 66 jfieldID mUrl; 67 } gWebHistoryItem; 68 69 struct WebBackForwardListFields { 70 jmethodID mAddHistoryItem; 71 jmethodID mRemoveHistoryItem; 72 jmethodID mSetCurrentIndex; 73 } gWebBackForwardList; 74 75 //-------------------------------------------------------------------------- 76 // WebBackForwardList native methods. 77 //-------------------------------------------------------------------------- 78 79 static void WebHistoryClose(JNIEnv* env, jobject obj, jint frame) 80 { 81 LOG_ASSERT(frame, "Close needs a valid Frame pointer!"); 82 WebCore::Frame* pFrame = (WebCore::Frame*)frame; 83 84 WebCore::BackForwardListImpl* list = static_cast<WebCore::BackForwardListImpl*>(pFrame->page()->backForwardList()); 85 RefPtr<WebCore::HistoryItem> current = list->currentItem(); 86 // Remove each item instead of using close(). close() is intended to be used 87 // right before the list is deleted. 88 WebCore::HistoryItemVector& entries = list->entries(); 89 int size = entries.size(); 90 for (int i = size - 1; i >= 0; --i) 91 list->removeItem(entries[i].get()); 92 // Add the current item back to the list. 93 if (current) { 94 current->setBridge(0); 95 // addItem will update the children to match the newly created bridge 96 list->addItem(current); 97 98 /* 99 * The Grand Prix site uses anchor navigations to change the display. 100 * WebKit tries to be smart and not load child frames that have the 101 * same history urls during an anchor navigation. This means that the 102 * current history item stored in the child frame's loader does not 103 * match the item found in the history tree. If we remove all the 104 * entries in the back/foward list, we have to restore the entire tree 105 * or else a HistoryItem might have a deleted parent. 106 * 107 * In order to restore the history tree correctly, we have to look up 108 * all the frames first and then look up the history item. We do this 109 * because the history item in the tree may be null at this point. 110 * Unfortunately, a HistoryItem can only search its immediately 111 * children so we do a breadth-first rebuild of the tree. 112 */ 113 114 // Keep a small list of child frames to traverse. 115 WTF::Vector<WebCore::Frame*> frameQueue; 116 // Fix the top-level item. 117 pFrame->loader()->history()->setCurrentItem(current.get()); 118 WebCore::Frame* child = pFrame->tree()->firstChild(); 119 // Remember the parent history item so we can search for a child item. 120 RefPtr<WebCore::HistoryItem> parent = current; 121 while (child) { 122 // Use the old history item since the current one may have a 123 // deleted parent. 124 WebCore::HistoryItem* item = parent->childItemWithTarget(child->tree()->name()); 125 child->loader()->history()->setCurrentItem(item); 126 // Append the first child to the queue if it exists. If there is no 127 // item, then we do not need to traverse the children since there 128 // will be no parent history item. 129 WebCore::Frame* firstChild; 130 if (item && (firstChild = child->tree()->firstChild())) 131 frameQueue.append(firstChild); 132 child = child->tree()->nextSibling(); 133 // If we don't have a sibling for this frame and the queue isn't 134 // empty, use the next entry in the queue. 135 if (!child && !frameQueue.isEmpty()) { 136 child = frameQueue.at(0); 137 frameQueue.remove(0); 138 // Figure out the parent history item used when searching for 139 // the history item to use. 140 parent = child->tree()->parent()->loader()->history()->currentItem(); 141 } 142 } 143 } 144 } 145 146 static void WebHistoryRestoreIndex(JNIEnv* env, jobject obj, jint frame, jint index) 147 { 148 LOG_ASSERT(frame, "RestoreState needs a valid Frame pointer!"); 149 WebCore::Frame* pFrame = (WebCore::Frame*)frame; 150 WebCore::Page* page = pFrame->page(); 151 WebCore::HistoryItem* currentItem = 152 static_cast<WebCore::BackForwardListImpl*>(page->backForwardList())->entries()[index].get(); 153 154 // load the current page with FrameLoadTypeIndexedBackForward so that it 155 // will use cache when it is possible 156 page->goToItem(currentItem, FrameLoadTypeIndexedBackForward); 157 } 158 159 static void WebHistoryInflate(JNIEnv* env, jobject obj, jint frame, jbyteArray data) 160 { 161 LOG_ASSERT(frame, "Inflate needs a valid frame pointer!"); 162 LOG_ASSERT(data, "Inflate needs a valid data pointer!"); 163 164 // Get the actual bytes and the length from the java array. 165 const jbyte* bytes = env->GetByteArrayElements(data, NULL); 166 jsize size = env->GetArrayLength(data); 167 168 // Inflate the history tree into one HistoryItem or null if the inflation 169 // failed. 170 RefPtr<WebCore::HistoryItem> newItem = WebCore::HistoryItem::create(); 171 WebHistoryItem* bridge = new WebHistoryItem(env, obj, newItem.get()); 172 newItem->setBridge(bridge); 173 174 // Inflate the item recursively. If it fails, that is ok. We'll have an 175 // incomplete HistoryItem but that is better than crashing due to a null 176 // item. 177 // We have a 2nd local variable since read_item_recursive may change the 178 // ptr's value. We can't pass &bytes since we have to send bytes to 179 // ReleaseByteArrayElements unchanged. 180 const char* ptr = reinterpret_cast<const char*>(bytes); 181 read_item_recursive(newItem.get(), &ptr, (int)size); 182 env->ReleaseByteArrayElements(data, const_cast<jbyte*>(bytes), JNI_ABORT); 183 bridge->setActive(); 184 185 // Add the new item to the back/forward list. 186 WebCore::Frame* pFrame = (WebCore::Frame*)frame; 187 pFrame->page()->backForwardList()->addItem(newItem); 188 189 // Update the item. 190 bridge->updateHistoryItem(newItem.get()); 191 } 192 193 // 6 empty strings + no document state + children count + 2 scales = 10 unsigned values 194 // 1 char for isTargetItem. 195 #define HISTORY_MIN_SIZE ((int)(sizeof(unsigned) * 10 + sizeof(char))) 196 197 jbyteArray WebHistory::Flatten(JNIEnv* env, WTF::Vector<char>& v, WebCore::HistoryItem* item) 198 { 199 if (!item) 200 return NULL; 201 202 // Reserve a vector of chars with an initial size of HISTORY_MIN_SIZE. 203 v.reserveCapacity(HISTORY_MIN_SIZE); 204 205 // Write the top-level history item and then write all the children 206 // recursively. 207 LOG_ASSERT(item->bridge(), "Why don't we have a bridge object here?"); 208 write_item(v, item); 209 write_children_recursive(v, item); 210 211 // Try to create a new java byte array. 212 jbyteArray b = env->NewByteArray(v.size()); 213 if (!b) 214 return NULL; 215 216 // Write our flattened data to the java array. 217 env->SetByteArrayRegion(b, 0, v.size(), (const jbyte*)v.data()); 218 return b; 219 } 220 221 WebHistoryItem::WebHistoryItem(JNIEnv* env, jobject obj, 222 WebCore::HistoryItem* item) : WebCore::AndroidWebHistoryBridge(item) { 223 m_object = env->NewWeakGlobalRef(obj); 224 m_parent = 0; 225 } 226 227 WebHistoryItem::~WebHistoryItem() { 228 if (m_object) { 229 JNIEnv* env = JSC::Bindings::getJNIEnv(); 230 if (!env) 231 return; 232 env->DeleteWeakGlobalRef(m_object); 233 } 234 } 235 236 void WebHistoryItem::updateHistoryItem(WebCore::HistoryItem* item) { 237 // Do not want to update during inflation. 238 if (!m_active) 239 return; 240 WebHistoryItem* webItem = this; 241 // Now we need to update the top-most WebHistoryItem based on the top-most 242 // HistoryItem. 243 if (m_parent) { 244 webItem = m_parent.get(); 245 if (webItem->hasOneRef()) { 246 // if the parent only has one ref, it is from this WebHistoryItem. 247 // This means that the matching WebCore::HistoryItem has been freed. 248 // This can happen during clear(). 249 LOGW("Can't updateHistoryItem as the top HistoryItem is gone"); 250 return; 251 } 252 while (webItem->parent()) 253 webItem = webItem->parent(); 254 item = webItem->historyItem(); 255 if (!item) { 256 // If a HistoryItem only exists for page cache, it is possible that 257 // the parent HistoryItem destroyed before the child HistoryItem. If 258 // it happens, skip updating. 259 LOGW("Can't updateHistoryItem as the top HistoryItem is gone"); 260 return; 261 } 262 } 263 JNIEnv* env = JSC::Bindings::getJNIEnv(); 264 if (!env) 265 return; 266 267 // Don't do anything if the item has been gc'd already 268 AutoJObject realItem = getRealObject(env, webItem->m_object); 269 if (!realItem.get()) 270 return; 271 272 const WTF::String urlString = WebFrame::convertIDNToUnicode(item->url()); 273 jstring urlStr = NULL; 274 if (!urlString.isNull()) 275 urlStr = wtfStringToJstring(env, urlString); 276 const WTF::String originalUrlString = WebFrame::convertIDNToUnicode(item->originalURL()); 277 jstring originalUrlStr = NULL; 278 if (!originalUrlString.isNull()) 279 originalUrlStr = wtfStringToJstring(env, originalUrlString); 280 const WTF::String& titleString = item->title(); 281 jstring titleStr = NULL; 282 if (!titleString.isNull()) 283 titleStr = wtfStringToJstring(env, titleString); 284 285 // Try to get the favicon from the history item. For some pages like Grand 286 // Prix, there are history items with anchors. If the icon fails for the 287 // item, try to get the icon using the url without the ref. 288 jobject favicon = NULL; 289 WTF::String url = item->urlString(); 290 if (item->url().hasFragmentIdentifier()) { 291 int refIndex = url.reverseFind('#'); 292 url = url.substring(0, refIndex); 293 } 294 // FIXME: This method should not be used from outside WebCore and will be removed. 295 // http://trac.webkit.org/changeset/81484 296 WebCore::Image* icon = WebCore::iconDatabase().synchronousIconForPageURL(url, WebCore::IntSize(16, 16)); 297 298 if (icon) 299 favicon = webcoreImageToJavaBitmap(env, icon); 300 301 WTF::Vector<char> data; 302 jbyteArray array = WebHistory::Flatten(env, data, item); 303 env->CallVoidMethod(realItem.get(), gWebHistoryItem.mUpdate, urlStr, 304 originalUrlStr, titleStr, favicon, array); 305 env->DeleteLocalRef(urlStr); 306 env->DeleteLocalRef(originalUrlStr); 307 env->DeleteLocalRef(titleStr); 308 if (favicon) 309 env->DeleteLocalRef(favicon); 310 env->DeleteLocalRef(array); 311 } 312 313 static void historyItemChanged(WebCore::HistoryItem* item) { 314 LOG_ASSERT(item, "historyItemChanged called with a null item"); 315 316 if (item->bridge()) 317 item->bridge()->updateHistoryItem(item); 318 } 319 320 void WebHistory::AddItem(const AutoJObject& list, WebCore::HistoryItem* item) 321 { 322 LOG_ASSERT(item, "newItem must take a valid HistoryItem!"); 323 // Item already added. Should only happen when we are inflating the list. 324 if (item->bridge() || !list.get()) 325 return; 326 327 JNIEnv* env = list.env(); 328 // Allocate a blank WebHistoryItem 329 jclass clazz = env->FindClass("android/webkit/WebHistoryItem"); 330 jobject newItem = env->NewObject(clazz, gWebHistoryItem.mInit); 331 env->DeleteLocalRef(clazz); 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 WTF::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, WTF::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 float scale = bridge->scale(); 420 LOGV("Writing scale %f", scale); 421 v.append((char*)&scale, sizeof(float)); 422 const float textWrapScale = bridge->textWrapScale(); 423 LOGV("Writing text wrap scale %f", textWrapScale); 424 v.append((char*)&textWrapScale, sizeof(float)); 425 426 // Scroll position. 427 const int scrollX = item->scrollPoint().x(); 428 v.append((char*)&scrollX, sizeof(int)); 429 const int scrollY = item->scrollPoint().y(); 430 v.append((char*)&scrollY, sizeof(int)); 431 432 // Document state 433 const WTF::Vector<WTF::String>& docState = item->documentState(); 434 WTF::Vector<WTF::String>::const_iterator end = docState.end(); 435 unsigned stateSize = docState.size(); 436 LOGV("Writing docState %d", stateSize); 437 v.append((char*)&stateSize, sizeof(unsigned)); 438 for (WTF::Vector<WTF::String>::const_iterator i = docState.begin(); i != end; ++i) { 439 write_string(v, *i); 440 } 441 442 // Is target item 443 LOGV("Writing isTargetItem %d", item->isTargetItem()); 444 v.append((char)item->isTargetItem()); 445 446 // Children count 447 unsigned childCount = item->children().size(); 448 LOGV("Writing childCount %d", childCount); 449 v.append((char*)&childCount, sizeof(unsigned)); 450 } 451 452 static void write_children_recursive(WTF::Vector<char>& v, WebCore::HistoryItem* parent) 453 { 454 const WebCore::HistoryItemVector& children = parent->children(); 455 WebCore::HistoryItemVector::const_iterator end = children.end(); 456 for (WebCore::HistoryItemVector::const_iterator i = children.begin(); i != end; ++i) { 457 WebCore::HistoryItem* item = (*i).get(); 458 LOG_ASSERT(parent->bridge(), 459 "The parent item should have a bridge object!"); 460 if (!item->bridge()) { 461 WebHistoryItem* bridge = new WebHistoryItem(static_cast<WebHistoryItem*>(parent->bridge())); 462 item->setBridge(bridge); 463 bridge->setActive(); 464 } else { 465 // The only time this item's parent may not be the same as the 466 // parent's bridge is during history close. In that case, the 467 // parent must not have a parent bridge. 468 WebHistoryItem* bridge = static_cast<WebHistoryItem*>(item->bridge()); 469 WebHistoryItem* parentBridge = static_cast<WebHistoryItem*>(parent->bridge()); 470 LOG_ASSERT(parentBridge->parent() == 0 || 471 bridge->parent() == parentBridge, 472 "Somehow this item has an incorrect parent"); 473 bridge->setParent(parentBridge); 474 } 475 write_item(v, item); 476 write_children_recursive(v, item); 477 } 478 } 479 480 static bool read_item_recursive(WebCore::HistoryItem* newItem, 481 const char** pData, int length) 482 { 483 if (!pData || length < HISTORY_MIN_SIZE) 484 return false; 485 486 const WebCore::TextEncoding& e = WebCore::UTF8Encoding(); 487 const char* data = *pData; 488 const char* end = data + length; 489 int sizeofUnsigned = (int)sizeof(unsigned); 490 491 // Read the original url 492 // Read the expected length of the string. 493 int l; 494 memcpy(&l, data, sizeofUnsigned); 495 // Increment data pointer by the size of an unsigned int. 496 data += sizeofUnsigned; 497 if (l) { 498 LOGV("Original url %d %.*s", l, l, data); 499 // If we have a length, check if that length exceeds the data length 500 // and return null if there is not enough data. 501 if (data + l < end) 502 newItem->setOriginalURLString(e.decode(data, l)); 503 else 504 return false; 505 // Increment the data pointer by the length of the string. 506 data += l; 507 } 508 // Check if we have enough data left to continue. 509 if (end - data < sizeofUnsigned) 510 return false; 511 512 // Read the url 513 memcpy(&l, data, sizeofUnsigned); 514 data += sizeofUnsigned; 515 if (l) { 516 LOGV("Url %d %.*s", l, l, data); 517 if (data + l < end) 518 newItem->setURLString(e.decode(data, l)); 519 else 520 return false; 521 data += l; 522 } 523 if (end - data < sizeofUnsigned) 524 return false; 525 526 // Read the title 527 memcpy(&l, data, sizeofUnsigned); 528 data += sizeofUnsigned; 529 if (l) { 530 LOGV("Title %d %.*s", l, l, data); 531 if (data + l < end) 532 newItem->setTitle(e.decode(data, l)); 533 else 534 return false; 535 data += l; 536 } 537 if (end - data < sizeofUnsigned) 538 return false; 539 540 // Generate a new ResourceRequest object for populating form information. 541 WTF::String formContentType; 542 WTF::PassRefPtr<WebCore::FormData> formData = NULL; 543 544 // Read the form content type 545 memcpy(&l, data, sizeofUnsigned); 546 data += sizeofUnsigned; 547 if (l) { 548 LOGV("Content type %d %.*s", l, l, data); 549 if (data + l < end) 550 formContentType = e.decode(data, l); 551 else 552 return false; 553 data += l; 554 } 555 if (end - data < sizeofUnsigned) 556 return false; 557 558 // Read the form data 559 memcpy(&l, data, sizeofUnsigned); 560 data += sizeofUnsigned; 561 if (l) { 562 LOGV("Form data %d %.*s", l, l, data); 563 if (data + l < end) 564 formData = WebCore::FormData::create(data, l); 565 else 566 return false; 567 data += l; 568 // Read the identifier 569 { 570 int64_t id; 571 int size = (int)sizeof(int64_t); 572 memcpy(&id, data, size); 573 data += size; 574 if (id) 575 formData->setIdentifier(id); 576 } 577 } 578 if (end - data < sizeofUnsigned) 579 return false; 580 581 // Set up the form info 582 if (formData != NULL) { 583 WebCore::ResourceRequest r; 584 r.setHTTPMethod("POST"); 585 r.setHTTPContentType(formContentType); 586 r.setHTTPBody(formData); 587 newItem->setFormInfoFromRequest(r); 588 } 589 590 // Read the target 591 memcpy(&l, data, sizeofUnsigned); 592 data += sizeofUnsigned; 593 if (l) { 594 LOGV("Target %d %.*s", l, l, data); 595 if (data + l < end) 596 newItem->setTarget(e.decode(data, l)); 597 else 598 return false; 599 data += l; 600 } 601 if (end - data < sizeofUnsigned) 602 return false; 603 604 AndroidWebHistoryBridge* bridge = newItem->bridge(); 605 LOG_ASSERT(bridge, "There should be a bridge object during inflate"); 606 float fValue; 607 // Read the screen scale 608 memcpy(&fValue, data, sizeof(float)); 609 LOGV("Screen scale %f", fValue); 610 bridge->setScale(fValue); 611 data += sizeof(float); 612 memcpy(&fValue, data, sizeofUnsigned); 613 LOGV("Text wrap scale %f", fValue); 614 bridge->setTextWrapScale(fValue); 615 data += sizeof(float); 616 617 if (end - data < sizeofUnsigned) 618 return false; 619 620 // Read scroll position. 621 int scrollX = 0; 622 memcpy(&scrollX, data, sizeofUnsigned); 623 data += sizeofUnsigned; 624 int scrollY = 0; 625 memcpy(&scrollY, data, sizeofUnsigned); 626 data += sizeofUnsigned; 627 newItem->setScrollPoint(IntPoint(scrollX, scrollY)); 628 629 if (end - data < sizeofUnsigned) 630 return false; 631 632 // Read the document state 633 memcpy(&l, data, sizeofUnsigned); 634 LOGV("Document state %d", l); 635 data += sizeofUnsigned; 636 if (l) { 637 // Check if we have enough data to at least parse the sizes of each 638 // document state string. 639 if (data + l * sizeofUnsigned >= end) 640 return false; 641 // Create a new vector and reserve enough space for the document state. 642 WTF::Vector<WTF::String> docState; 643 docState.reserveCapacity(l); 644 while (l--) { 645 // Check each time if we have enough to parse the length of the next 646 // string. 647 if (end - data < sizeofUnsigned) 648 return false; 649 int strLen; 650 memcpy(&strLen, data, sizeofUnsigned); 651 data += sizeofUnsigned; 652 if (data + strLen < end) 653 docState.append(e.decode(data, strLen)); 654 else 655 return false; 656 LOGV("\t\t%d %.*s", strLen, strLen, data); 657 data += strLen; 658 } 659 newItem->setDocumentState(docState); 660 } 661 // Check if we have enough to read the next byte 662 if (data >= end) 663 return false; 664 665 // Read is target item 666 // Cast the value to unsigned char in order to make a negative value larger 667 // than 1. A value that is not 0 or 1 is a failure. 668 unsigned char c = (unsigned char)data[0]; 669 if (c > 1) 670 return false; 671 LOGV("Target item %d", c); 672 newItem->setIsTargetItem((bool)c); 673 data++; 674 if (end - data < sizeofUnsigned) 675 return false; 676 677 // Read the child count 678 memcpy(&l, data, sizeofUnsigned); 679 LOGV("Child count %d", l); 680 data += sizeofUnsigned; 681 *pData = data; 682 if (l) { 683 // Check if we have the minimum amount need to parse l children. 684 if (data + l * HISTORY_MIN_SIZE >= end) 685 return false; 686 while (l--) { 687 // No need to check the length each time because read_item_recursive 688 // will return null if there isn't enough data left to parse. 689 WTF::PassRefPtr<WebCore::HistoryItem> child = WebCore::HistoryItem::create(); 690 // Set a bridge that will not call into java. 691 child->setBridge(new WebHistoryItem(static_cast<WebHistoryItem*>(bridge))); 692 // Read the child item. 693 if (!read_item_recursive(child.get(), pData, end - data)) { 694 child.clear(); 695 return false; 696 } 697 child->bridge()->setActive(); 698 newItem->addChildItem(child); 699 } 700 } 701 return true; 702 } 703 704 // On arm, this test will cause memory corruption since converting char* will 705 // byte align the result and this test does not use memset (it probably 706 // should). 707 // On the simulator, using HistoryItem will invoke the IconDatabase which will 708 // initialize the main thread. Since this is invoked by the Zygote process, the 709 // main thread will be incorrect and an assert will fire later. 710 // In conclusion, define UNIT_TEST only if you know what you are doing. 711 #ifdef UNIT_TEST 712 static void unit_test() 713 { 714 LOGD("Entering history unit test!"); 715 const char* test1 = new char[0]; 716 WTF::RefPtr<WebCore::HistoryItem> item = WebCore::HistoryItem::create(); 717 WebCore::HistoryItem* testItem = item.get(); 718 testItem->setBridge(new WebHistoryItem(0)); 719 LOG_ASSERT(!read_item_recursive(testItem, &test1, 0), "0 length array should fail!"); 720 delete[] test1; 721 const char* test2 = new char[2]; 722 LOG_ASSERT(!read_item_recursive(testItem, &test2, 2), "Small array should fail!"); 723 delete[] test2; 724 LOG_ASSERT(!read_item_recursive(testItem, NULL, HISTORY_MIN_SIZE), "Null data should fail!"); 725 // Original Url 726 char* test3 = new char[HISTORY_MIN_SIZE]; 727 const char* ptr = (const char*)test3; 728 memset(test3, 0, HISTORY_MIN_SIZE); 729 *(int*)test3 = 4000; 730 LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length originalUrl should fail!"); 731 // Url 732 int offset = 4; 733 memset(test3, 0, HISTORY_MIN_SIZE); 734 ptr = (const char*)test3; 735 *(int*)(test3 + offset) = 4000; 736 LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length url should fail!"); 737 // Title 738 offset += 4; 739 memset(test3, 0, HISTORY_MIN_SIZE); 740 ptr = (const char*)test3; 741 *(int*)(test3 + offset) = 4000; 742 LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length title should fail!"); 743 // Form content type 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 contentType should fail!"); 749 // Form data 750 offset += 4; 751 memset(test3, 0, HISTORY_MIN_SIZE); 752 ptr = (const char*)test3; 753 *(int*)(test3 + offset) = 4000; 754 LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length form data should fail!"); 755 // Target 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 length target should fail!"); 761 offset += 4; // Scale 762 // Document state 763 offset += 4; 764 memset(test3, 0, HISTORY_MIN_SIZE); 765 ptr = (const char*)test3; 766 *(int*)(test3 + offset) = 4000; 767 LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 length document state should fail!"); 768 // Is target item 769 offset += 1; 770 memset(test3, 0, HISTORY_MIN_SIZE); 771 ptr = (const char*)test3; 772 *(char*)(test3 + offset) = '!'; 773 LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "IsTargetItem should fail with ! as the value!"); 774 // Child count 775 offset += 4; 776 memset(test3, 0, HISTORY_MIN_SIZE); 777 ptr = (const char*)test3; 778 *(int*)(test3 + offset) = 4000; 779 LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE), "4000 kids should fail!"); 780 offset = 36; 781 // Test document state 782 delete[] test3; 783 test3 = new char[HISTORY_MIN_SIZE + sizeof(unsigned)]; 784 memset(test3, 0, HISTORY_MIN_SIZE + sizeof(unsigned)); 785 ptr = (const char*)test3; 786 *(int*)(test3 + offset) = 1; 787 *(int*)(test3 + offset + 4) = 20; 788 LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE + sizeof(unsigned)), "1 20 length document state string should fail!"); 789 delete[] test3; 790 test3 = new char[HISTORY_MIN_SIZE + 2 * sizeof(unsigned)]; 791 memset(test3, 0, HISTORY_MIN_SIZE + 2 * sizeof(unsigned)); 792 ptr = (const char*)test3; 793 *(int*)(test3 + offset) = 2; 794 *(int*)(test3 + offset + 4) = 0; 795 *(int*)(test3 + offset + 8) = 20; 796 LOG_ASSERT(!read_item_recursive(testItem, &ptr, HISTORY_MIN_SIZE + 2 * sizeof(unsigned) ), "2 20 length document state string should fail!"); 797 delete[] test3; 798 } 799 #endif 800 801 //--------------------------------------------------------- 802 // JNI registration 803 //--------------------------------------------------------- 804 static JNINativeMethod gWebBackForwardListMethods[] = { 805 { "nativeClose", "(I)V", 806 (void*) WebHistoryClose }, 807 { "restoreIndex", "(II)V", 808 (void*) WebHistoryRestoreIndex } 809 }; 810 811 static JNINativeMethod gWebHistoryItemMethods[] = { 812 { "inflate", "(I[B)V", 813 (void*) WebHistoryInflate } 814 }; 815 816 int registerWebHistory(JNIEnv* env) 817 { 818 // Get notified of all changes to history items. 819 WebCore::notifyHistoryItemChanged = historyItemChanged; 820 #ifdef UNIT_TEST 821 unit_test(); 822 #endif 823 // Find WebHistoryItem, its constructor, and the update method. 824 jclass clazz = env->FindClass("android/webkit/WebHistoryItem"); 825 LOG_ASSERT(clazz, "Unable to find class android/webkit/WebHistoryItem"); 826 gWebHistoryItem.mInit = env->GetMethodID(clazz, "<init>", "()V"); 827 LOG_ASSERT(gWebHistoryItem.mInit, "Could not find WebHistoryItem constructor"); 828 gWebHistoryItem.mUpdate = env->GetMethodID(clazz, "update", 829 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/graphics/Bitmap;[B)V"); 830 LOG_ASSERT(gWebHistoryItem.mUpdate, "Could not find method update in WebHistoryItem"); 831 832 // Find the field ids for mTitle and mUrl. 833 gWebHistoryItem.mTitle = env->GetFieldID(clazz, "mTitle", "Ljava/lang/String;"); 834 LOG_ASSERT(gWebHistoryItem.mTitle, "Could not find field mTitle in WebHistoryItem"); 835 gWebHistoryItem.mUrl = env->GetFieldID(clazz, "mUrl", "Ljava/lang/String;"); 836 LOG_ASSERT(gWebHistoryItem.mUrl, "Could not find field mUrl in WebHistoryItem"); 837 env->DeleteLocalRef(clazz); 838 839 // Find the WebBackForwardList object and method. 840 clazz = env->FindClass("android/webkit/WebBackForwardList"); 841 LOG_ASSERT(clazz, "Unable to find class android/webkit/WebBackForwardList"); 842 gWebBackForwardList.mAddHistoryItem = env->GetMethodID(clazz, "addHistoryItem", 843 "(Landroid/webkit/WebHistoryItem;)V"); 844 LOG_ASSERT(gWebBackForwardList.mAddHistoryItem, "Could not find method addHistoryItem"); 845 gWebBackForwardList.mRemoveHistoryItem = env->GetMethodID(clazz, "removeHistoryItem", 846 "(I)V"); 847 LOG_ASSERT(gWebBackForwardList.mRemoveHistoryItem, "Could not find method removeHistoryItem"); 848 gWebBackForwardList.mSetCurrentIndex = env->GetMethodID(clazz, "setCurrentIndex", "(I)V"); 849 LOG_ASSERT(gWebBackForwardList.mSetCurrentIndex, "Could not find method setCurrentIndex"); 850 env->DeleteLocalRef(clazz); 851 852 int result = jniRegisterNativeMethods(env, "android/webkit/WebBackForwardList", 853 gWebBackForwardListMethods, NELEM(gWebBackForwardListMethods)); 854 return (result < 0) ? result : jniRegisterNativeMethods(env, "android/webkit/WebHistoryItem", 855 gWebHistoryItemMethods, NELEM(gWebHistoryItemMethods)); 856 } 857 858 } /* namespace android */ 859