1/* 2 * Copyright (C) 2005, 2006, 2008, 2011 Apple Inc. All rights reserved. 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 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. 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 APPLE COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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#include "config.h" 27#include "HistoryItem.h" 28 29#include "CachedPage.h" 30#include "Document.h" 31#include "IconDatabase.h" 32#include "PageCache.h" 33#include "ResourceRequest.h" 34#include "SharedBuffer.h" 35#include <stdio.h> 36#include <wtf/CurrentTime.h> 37#include <wtf/Decoder.h> 38#include <wtf/Encoder.h> 39#include <wtf/MathExtras.h> 40#include <wtf/text/CString.h> 41 42namespace WebCore { 43 44const uint32_t backForwardTreeEncodingVersion = 2; 45 46static long long generateSequenceNumber() 47{ 48 // Initialize to the current time to reduce the likelihood of generating 49 // identifiers that overlap with those from past/future browser sessions. 50 static long long next = static_cast<long long>(currentTime() * 1000000.0); 51 return ++next; 52} 53 54static void defaultNotifyHistoryItemChanged(HistoryItem*) 55{ 56} 57 58void (*notifyHistoryItemChanged)(HistoryItem*) = defaultNotifyHistoryItemChanged; 59 60HistoryItem::HistoryItem() 61 : m_lastVisitedTime(0) 62 , m_lastVisitWasHTTPNonGet(false) 63 , m_pageScaleFactor(1) 64 , m_lastVisitWasFailure(false) 65 , m_isTargetItem(false) 66 , m_visitCount(0) 67 , m_itemSequenceNumber(generateSequenceNumber()) 68 , m_documentSequenceNumber(generateSequenceNumber()) 69 , m_next(0) 70 , m_prev(0) 71{ 72} 73 74HistoryItem::HistoryItem(const String& urlString, const String& title, double time) 75 : m_urlString(urlString) 76 , m_originalURLString(urlString) 77 , m_title(title) 78 , m_lastVisitedTime(time) 79 , m_lastVisitWasHTTPNonGet(false) 80 , m_pageScaleFactor(1) 81 , m_lastVisitWasFailure(false) 82 , m_isTargetItem(false) 83 , m_visitCount(0) 84 , m_itemSequenceNumber(generateSequenceNumber()) 85 , m_documentSequenceNumber(generateSequenceNumber()) 86 , m_next(0) 87 , m_prev(0) 88{ 89 iconDatabase().retainIconForPageURL(m_urlString); 90} 91 92HistoryItem::HistoryItem(const String& urlString, const String& title, const String& alternateTitle, double time) 93 : m_urlString(urlString) 94 , m_originalURLString(urlString) 95 , m_title(title) 96 , m_displayTitle(alternateTitle) 97 , m_lastVisitedTime(time) 98 , m_lastVisitWasHTTPNonGet(false) 99 , m_pageScaleFactor(1) 100 , m_lastVisitWasFailure(false) 101 , m_isTargetItem(false) 102 , m_visitCount(0) 103 , m_itemSequenceNumber(generateSequenceNumber()) 104 , m_documentSequenceNumber(generateSequenceNumber()) 105 , m_next(0) 106 , m_prev(0) 107{ 108 iconDatabase().retainIconForPageURL(m_urlString); 109} 110 111HistoryItem::HistoryItem(const KURL& url, const String& target, const String& parent, const String& title) 112 : m_urlString(url.string()) 113 , m_originalURLString(url.string()) 114 , m_target(target) 115 , m_parent(parent) 116 , m_title(title) 117 , m_lastVisitedTime(0) 118 , m_lastVisitWasHTTPNonGet(false) 119 , m_pageScaleFactor(1) 120 , m_lastVisitWasFailure(false) 121 , m_isTargetItem(false) 122 , m_visitCount(0) 123 , m_itemSequenceNumber(generateSequenceNumber()) 124 , m_documentSequenceNumber(generateSequenceNumber()) 125 , m_next(0) 126 , m_prev(0) 127{ 128 iconDatabase().retainIconForPageURL(m_urlString); 129} 130 131HistoryItem::~HistoryItem() 132{ 133 ASSERT(!m_cachedPage); 134 iconDatabase().releaseIconForPageURL(m_urlString); 135#if PLATFORM(ANDROID) 136 if (m_bridge) 137 m_bridge->detachHistoryItem(); 138#endif 139} 140 141inline HistoryItem::HistoryItem(const HistoryItem& item) 142 : RefCounted<HistoryItem>() 143 , m_urlString(item.m_urlString) 144 , m_originalURLString(item.m_originalURLString) 145 , m_referrer(item.m_referrer) 146 , m_target(item.m_target) 147 , m_parent(item.m_parent) 148 , m_title(item.m_title) 149 , m_displayTitle(item.m_displayTitle) 150 , m_lastVisitedTime(item.m_lastVisitedTime) 151 , m_lastVisitWasHTTPNonGet(item.m_lastVisitWasHTTPNonGet) 152 , m_scrollPoint(item.m_scrollPoint) 153 , m_pageScaleFactor(item.m_pageScaleFactor) 154 , m_lastVisitWasFailure(item.m_lastVisitWasFailure) 155 , m_isTargetItem(item.m_isTargetItem) 156 , m_visitCount(item.m_visitCount) 157 , m_dailyVisitCounts(item.m_dailyVisitCounts) 158 , m_weeklyVisitCounts(item.m_weeklyVisitCounts) 159 , m_itemSequenceNumber(item.m_itemSequenceNumber) 160 , m_documentSequenceNumber(item.m_documentSequenceNumber) 161 , m_formContentType(item.m_formContentType) 162{ 163 if (item.m_formData) 164 m_formData = item.m_formData->copy(); 165 166 unsigned size = item.m_children.size(); 167 m_children.reserveInitialCapacity(size); 168 for (unsigned i = 0; i < size; ++i) 169 m_children.uncheckedAppend(item.m_children[i]->copy()); 170 171 if (item.m_redirectURLs) 172 m_redirectURLs = adoptPtr(new Vector<String>(*item.m_redirectURLs)); 173} 174 175PassRefPtr<HistoryItem> HistoryItem::copy() const 176{ 177 return adoptRef(new HistoryItem(*this)); 178} 179 180void HistoryItem::reset() 181{ 182 iconDatabase().releaseIconForPageURL(m_urlString); 183 184 m_urlString = String(); 185 m_originalURLString = String(); 186 m_referrer = String(); 187 m_target = String(); 188 m_parent = String(); 189 m_title = String(); 190 m_displayTitle = String(); 191 192 m_lastVisitedTime = 0; 193 m_lastVisitWasHTTPNonGet = false; 194 195 m_lastVisitWasFailure = false; 196 m_isTargetItem = false; 197 m_visitCount = 0; 198 m_dailyVisitCounts.clear(); 199 m_weeklyVisitCounts.clear(); 200 201 m_redirectURLs.clear(); 202 203 m_itemSequenceNumber = generateSequenceNumber(); 204 205 m_stateObject = 0; 206 m_documentSequenceNumber = generateSequenceNumber(); 207 208 m_formData = 0; 209 m_formContentType = String(); 210} 211 212const String& HistoryItem::urlString() const 213{ 214 return m_urlString; 215} 216 217// The first URL we loaded to get to where this history item points. Includes both client 218// and server redirects. 219const String& HistoryItem::originalURLString() const 220{ 221 return m_originalURLString; 222} 223 224const String& HistoryItem::title() const 225{ 226 return m_title; 227} 228 229const String& HistoryItem::alternateTitle() const 230{ 231 return m_displayTitle; 232} 233 234double HistoryItem::lastVisitedTime() const 235{ 236 return m_lastVisitedTime; 237} 238 239KURL HistoryItem::url() const 240{ 241 return KURL(ParsedURLString, m_urlString); 242} 243 244KURL HistoryItem::originalURL() const 245{ 246 return KURL(ParsedURLString, m_originalURLString); 247} 248 249const String& HistoryItem::referrer() const 250{ 251 return m_referrer; 252} 253 254const String& HistoryItem::target() const 255{ 256 return m_target; 257} 258 259const String& HistoryItem::parent() const 260{ 261 return m_parent; 262} 263 264void HistoryItem::setAlternateTitle(const String& alternateTitle) 265{ 266 m_displayTitle = alternateTitle; 267 notifyHistoryItemChanged(this); 268} 269 270void HistoryItem::setURLString(const String& urlString) 271{ 272 if (m_urlString != urlString) { 273 iconDatabase().releaseIconForPageURL(m_urlString); 274 m_urlString = urlString; 275 iconDatabase().retainIconForPageURL(m_urlString); 276 } 277 278 notifyHistoryItemChanged(this); 279} 280 281void HistoryItem::setURL(const KURL& url) 282{ 283 pageCache()->remove(this); 284 setURLString(url.string()); 285 clearDocumentState(); 286} 287 288void HistoryItem::setOriginalURLString(const String& urlString) 289{ 290 m_originalURLString = urlString; 291 notifyHistoryItemChanged(this); 292} 293 294void HistoryItem::setReferrer(const String& referrer) 295{ 296 m_referrer = referrer; 297 notifyHistoryItemChanged(this); 298} 299 300void HistoryItem::setTitle(const String& title) 301{ 302 m_title = title; 303 notifyHistoryItemChanged(this); 304} 305 306void HistoryItem::setTarget(const String& target) 307{ 308 m_target = target; 309 notifyHistoryItemChanged(this); 310} 311 312void HistoryItem::setParent(const String& parent) 313{ 314 m_parent = parent; 315} 316 317static inline int timeToDay(double time) 318{ 319 static const double secondsPerDay = 60 * 60 * 24; 320 return static_cast<int>(ceil(time / secondsPerDay)); 321} 322 323void HistoryItem::padDailyCountsForNewVisit(double time) 324{ 325 if (m_dailyVisitCounts.isEmpty()) 326 m_dailyVisitCounts.prepend(m_visitCount); 327 328 int daysElapsed = timeToDay(time) - timeToDay(m_lastVisitedTime); 329 330 if (daysElapsed < 0) 331 daysElapsed = 0; 332 333 Vector<int> padding; 334 padding.fill(0, daysElapsed); 335 m_dailyVisitCounts.prepend(padding); 336} 337 338static const size_t daysPerWeek = 7; 339static const size_t maxDailyCounts = 2 * daysPerWeek - 1; 340static const size_t maxWeeklyCounts = 5; 341 342void HistoryItem::collapseDailyVisitsToWeekly() 343{ 344 while (m_dailyVisitCounts.size() > maxDailyCounts) { 345 int oldestWeekTotal = 0; 346 for (size_t i = 0; i < daysPerWeek; i++) 347 oldestWeekTotal += m_dailyVisitCounts[m_dailyVisitCounts.size() - daysPerWeek + i]; 348 m_dailyVisitCounts.shrink(m_dailyVisitCounts.size() - daysPerWeek); 349 m_weeklyVisitCounts.prepend(oldestWeekTotal); 350 } 351 352 if (m_weeklyVisitCounts.size() > maxWeeklyCounts) 353 m_weeklyVisitCounts.shrink(maxWeeklyCounts); 354} 355 356void HistoryItem::recordVisitAtTime(double time, VisitCountBehavior visitCountBehavior) 357{ 358 padDailyCountsForNewVisit(time); 359 360 m_lastVisitedTime = time; 361 362 if (visitCountBehavior == IncreaseVisitCount) { 363 ++m_visitCount; 364 ++m_dailyVisitCounts[0]; 365 } 366 367 collapseDailyVisitsToWeekly(); 368} 369 370void HistoryItem::setLastVisitedTime(double time) 371{ 372 if (m_lastVisitedTime != time) 373 recordVisitAtTime(time); 374} 375 376void HistoryItem::visited(const String& title, double time, VisitCountBehavior visitCountBehavior) 377{ 378 m_title = title; 379 recordVisitAtTime(time, visitCountBehavior); 380} 381 382int HistoryItem::visitCount() const 383{ 384 return m_visitCount; 385} 386 387void HistoryItem::recordInitialVisit() 388{ 389 ASSERT(!m_visitCount); 390 recordVisitAtTime(m_lastVisitedTime); 391} 392 393void HistoryItem::setVisitCount(int count) 394{ 395 m_visitCount = count; 396} 397 398void HistoryItem::adoptVisitCounts(Vector<int>& dailyCounts, Vector<int>& weeklyCounts) 399{ 400 m_dailyVisitCounts.clear(); 401 m_dailyVisitCounts.swap(dailyCounts); 402 m_weeklyVisitCounts.clear(); 403 m_weeklyVisitCounts.swap(weeklyCounts); 404} 405 406const IntPoint& HistoryItem::scrollPoint() const 407{ 408 return m_scrollPoint; 409} 410 411void HistoryItem::setScrollPoint(const IntPoint& point) 412{ 413 m_scrollPoint = point; 414} 415 416void HistoryItem::clearScrollPoint() 417{ 418 m_scrollPoint.setX(0); 419 m_scrollPoint.setY(0); 420} 421 422float HistoryItem::pageScaleFactor() const 423{ 424 return m_pageScaleFactor; 425} 426 427void HistoryItem::setPageScaleFactor(float scaleFactor) 428{ 429 m_pageScaleFactor = scaleFactor; 430} 431 432void HistoryItem::setDocumentState(const Vector<String>& state) 433{ 434 m_documentState = state; 435#if PLATFORM(ANDROID) 436 notifyHistoryItemChanged(this); 437#endif 438} 439 440const Vector<String>& HistoryItem::documentState() const 441{ 442 return m_documentState; 443} 444 445void HistoryItem::clearDocumentState() 446{ 447 m_documentState.clear(); 448#if PLATFORM(ANDROID) 449 notifyHistoryItemChanged(this); 450#endif 451} 452 453bool HistoryItem::isTargetItem() const 454{ 455 return m_isTargetItem; 456} 457 458void HistoryItem::setIsTargetItem(bool flag) 459{ 460 m_isTargetItem = flag; 461#if PLATFORM(ANDROID) 462 notifyHistoryItemChanged(this); 463#endif 464} 465 466void HistoryItem::setStateObject(PassRefPtr<SerializedScriptValue> object) 467{ 468 m_stateObject = object; 469} 470 471void HistoryItem::addChildItem(PassRefPtr<HistoryItem> child) 472{ 473 ASSERT(!childItemWithTarget(child->target())); 474 m_children.append(child); 475#if PLATFORM(ANDROID) 476 notifyHistoryItemChanged(this); 477#endif 478} 479 480void HistoryItem::setChildItem(PassRefPtr<HistoryItem> child) 481{ 482 ASSERT(!child->isTargetItem()); 483 unsigned size = m_children.size(); 484 for (unsigned i = 0; i < size; ++i) { 485 if (m_children[i]->target() == child->target()) { 486 child->setIsTargetItem(m_children[i]->isTargetItem()); 487 m_children[i] = child; 488 return; 489 } 490 } 491 m_children.append(child); 492} 493 494HistoryItem* HistoryItem::childItemWithTarget(const String& target) const 495{ 496 unsigned size = m_children.size(); 497 for (unsigned i = 0; i < size; ++i) { 498 if (m_children[i]->target() == target) 499 return m_children[i].get(); 500 } 501 return 0; 502} 503 504HistoryItem* HistoryItem::childItemWithDocumentSequenceNumber(long long number) const 505{ 506 unsigned size = m_children.size(); 507 for (unsigned i = 0; i < size; ++i) { 508 if (m_children[i]->documentSequenceNumber() == number) 509 return m_children[i].get(); 510 } 511 return 0; 512} 513 514// <rdar://problem/4895849> HistoryItem::findTargetItem() should be replaced with a non-recursive method. 515HistoryItem* HistoryItem::findTargetItem() 516{ 517 if (m_isTargetItem) 518 return this; 519 unsigned size = m_children.size(); 520 for (unsigned i = 0; i < size; ++i) { 521 if (HistoryItem* match = m_children[i]->targetItem()) 522 return match; 523 } 524 return 0; 525} 526 527HistoryItem* HistoryItem::targetItem() 528{ 529 HistoryItem* foundItem = findTargetItem(); 530 return foundItem ? foundItem : this; 531} 532 533const HistoryItemVector& HistoryItem::children() const 534{ 535 return m_children; 536} 537 538bool HistoryItem::hasChildren() const 539{ 540 return !m_children.isEmpty(); 541} 542 543void HistoryItem::clearChildren() 544{ 545 m_children.clear(); 546} 547 548// We do same-document navigation if going to a different item and if either of the following is true: 549// - The other item corresponds to the same document (for history entries created via pushState or fragment changes). 550// - The other item corresponds to the same set of documents, including frames (for history entries created via regular navigation) 551bool HistoryItem::shouldDoSameDocumentNavigationTo(HistoryItem* otherItem) const 552{ 553 if (this == otherItem) 554 return false; 555 556 if (stateObject() || otherItem->stateObject()) 557 return documentSequenceNumber() == otherItem->documentSequenceNumber(); 558 559 if ((url().hasFragmentIdentifier() || otherItem->url().hasFragmentIdentifier()) && equalIgnoringFragmentIdentifier(url(), otherItem->url())) 560 return documentSequenceNumber() == otherItem->documentSequenceNumber(); 561 562 return hasSameDocumentTree(otherItem); 563} 564 565// Does a recursive check that this item and its descendants have the same 566// document sequence numbers as the other item. 567bool HistoryItem::hasSameDocumentTree(HistoryItem* otherItem) const 568{ 569 if (documentSequenceNumber() != otherItem->documentSequenceNumber()) 570 return false; 571 572 if (children().size() != otherItem->children().size()) 573 return false; 574 575 for (size_t i = 0; i < children().size(); i++) { 576 HistoryItem* child = children()[i].get(); 577 HistoryItem* otherChild = otherItem->childItemWithDocumentSequenceNumber(child->documentSequenceNumber()); 578 if (!otherChild || !child->hasSameDocumentTree(otherChild)) 579 return false; 580 } 581 582 return true; 583} 584 585// Does a non-recursive check that this item and its immediate children have the 586// same frames as the other item. 587bool HistoryItem::hasSameFrames(HistoryItem* otherItem) const 588{ 589 if (target() != otherItem->target()) 590 return false; 591 592 if (children().size() != otherItem->children().size()) 593 return false; 594 595 for (size_t i = 0; i < children().size(); i++) { 596 if (!otherItem->childItemWithTarget(children()[i]->target())) 597 return false; 598 } 599 600 return true; 601} 602 603String HistoryItem::formContentType() const 604{ 605 return m_formContentType; 606} 607 608void HistoryItem::setFormInfoFromRequest(const ResourceRequest& request) 609{ 610 m_referrer = request.httpReferrer(); 611 612 if (equalIgnoringCase(request.httpMethod(), "POST")) { 613 // FIXME: Eventually we have to make this smart enough to handle the case where 614 // we have a stream for the body to handle the "data interspersed with files" feature. 615 m_formData = request.httpBody(); 616 m_formContentType = request.httpContentType(); 617 } else { 618 m_formData = 0; 619 m_formContentType = String(); 620 } 621#if PLATFORM(ANDROID) 622 notifyHistoryItemChanged(this); 623#endif 624} 625 626void HistoryItem::setFormData(PassRefPtr<FormData> formData) 627{ 628 m_formData = formData; 629} 630 631void HistoryItem::setFormContentType(const String& formContentType) 632{ 633 m_formContentType = formContentType; 634} 635 636FormData* HistoryItem::formData() 637{ 638 return m_formData.get(); 639} 640 641bool HistoryItem::isCurrentDocument(Document* doc) const 642{ 643 // FIXME: We should find a better way to check if this is the current document. 644 return equalIgnoringFragmentIdentifier(url(), doc->url()); 645} 646 647void HistoryItem::mergeAutoCompleteHints(HistoryItem* otherItem) 648{ 649 // FIXME: this is broken - we should be merging the daily counts 650 // somehow. but this is to support API that's not really used in 651 // practice so leave it broken for now. 652 ASSERT(otherItem); 653 if (otherItem != this) 654 m_visitCount += otherItem->m_visitCount; 655} 656 657void HistoryItem::addRedirectURL(const String& url) 658{ 659 if (!m_redirectURLs) 660 m_redirectURLs = adoptPtr(new Vector<String>); 661 662 // Our API allows us to store all the URLs in the redirect chain, but for 663 // now we only have a use for the final URL. 664 (*m_redirectURLs).resize(1); 665 (*m_redirectURLs)[0] = url; 666} 667 668Vector<String>* HistoryItem::redirectURLs() const 669{ 670 return m_redirectURLs.get(); 671} 672 673void HistoryItem::setRedirectURLs(PassOwnPtr<Vector<String> > redirectURLs) 674{ 675 m_redirectURLs = redirectURLs; 676} 677 678void HistoryItem::encodeBackForwardTree(Encoder& encoder) const 679{ 680 encoder.encodeUInt32(backForwardTreeEncodingVersion); 681 682 encodeBackForwardTreeNode(encoder); 683} 684 685void HistoryItem::encodeBackForwardTreeNode(Encoder& encoder) const 686{ 687 size_t size = m_children.size(); 688 encoder.encodeUInt64(size); 689 for (size_t i = 0; i < size; ++i) { 690 const HistoryItem& child = *m_children[i]; 691 692 encoder.encodeString(child.m_originalURLString); 693 694 encoder.encodeString(child.m_urlString); 695 696 child.encodeBackForwardTreeNode(encoder); 697 } 698 699 encoder.encodeInt64(m_documentSequenceNumber); 700 701 size = m_documentState.size(); 702 encoder.encodeUInt64(size); 703 for (size_t i = 0; i < size; ++i) 704 encoder.encodeString(m_documentState[i]); 705 706 encoder.encodeString(m_formContentType); 707 708 encoder.encodeBool(m_formData); 709 if (m_formData) 710 m_formData->encodeForBackForward(encoder); 711 712 encoder.encodeInt64(m_itemSequenceNumber); 713 714 encoder.encodeString(m_referrer); 715 716 encoder.encodeInt32(m_scrollPoint.x()); 717 encoder.encodeInt32(m_scrollPoint.y()); 718 719 encoder.encodeFloat(m_pageScaleFactor); 720 721 encoder.encodeBool(m_stateObject); 722 if (m_stateObject) { 723#if !USE(V8) 724 encoder.encodeBytes(m_stateObject->data().data(), m_stateObject->data().size()); 725#else 726 encoder.encodeString(m_stateObject->toWireString()); 727#endif 728 } 729 730 encoder.encodeString(m_target); 731} 732 733struct DecodeRecursionStackElement { 734 RefPtr<HistoryItem> node; 735 size_t i; 736 uint64_t size; 737 738 DecodeRecursionStackElement(PassRefPtr<HistoryItem> node, size_t i, uint64_t size) 739 : node(node) 740 , i(i) 741 , size(size) 742 { 743 } 744}; 745 746PassRefPtr<HistoryItem> HistoryItem::decodeBackForwardTree(const String& topURLString, const String& topTitle, const String& topOriginalURLString, Decoder& decoder) 747{ 748 // Since the data stream is not trusted, the decode has to be non-recursive. 749 // We don't want bad data to cause a stack overflow. 750 751 uint32_t version; 752 if (!decoder.decodeUInt32(version)) 753 return 0; 754 if (version != backForwardTreeEncodingVersion) 755 return 0; 756 757 String urlString = topURLString; 758 String title = topTitle; 759 String originalURLString = topOriginalURLString; 760 761 Vector<DecodeRecursionStackElement, 16> recursionStack; 762 763recurse: 764 RefPtr<HistoryItem> node = create(urlString, title, 0); 765 766 node->setOriginalURLString(originalURLString); 767 768 title = String(); 769 770 uint64_t size; 771 if (!decoder.decodeUInt64(size)) 772 return 0; 773 size_t i; 774 RefPtr<HistoryItem> child; 775 for (i = 0; i < size; ++i) { 776 if (!decoder.decodeString(originalURLString)) 777 return 0; 778 779 if (!decoder.decodeString(urlString)) 780 return 0; 781 782 recursionStack.append(DecodeRecursionStackElement(node.release(), i, size)); 783 goto recurse; 784 785resume: 786 node->m_children.append(child.release()); 787 } 788 789 if (!decoder.decodeInt64(node->m_documentSequenceNumber)) 790 return 0; 791 792 if (!decoder.decodeUInt64(size)) 793 return 0; 794 for (i = 0; i < size; ++i) { 795 String state; 796 if (!decoder.decodeString(state)) 797 return 0; 798 node->m_documentState.append(state); 799 } 800 801 if (!decoder.decodeString(node->m_formContentType)) 802 return 0; 803 804 bool hasFormData; 805 if (!decoder.decodeBool(hasFormData)) 806 return 0; 807 if (hasFormData) { 808 node->m_formData = FormData::decodeForBackForward(decoder); 809 if (!node->m_formData) 810 return 0; 811 } 812 813 if (!decoder.decodeInt64(node->m_itemSequenceNumber)) 814 return 0; 815 816 if (!decoder.decodeString(node->m_referrer)) 817 return 0; 818 819 int32_t x; 820 if (!decoder.decodeInt32(x)) 821 return 0; 822 int32_t y; 823 if (!decoder.decodeInt32(y)) 824 return 0; 825 node->m_scrollPoint = IntPoint(x, y); 826 827 if (!decoder.decodeFloat(node->m_pageScaleFactor)) 828 return 0; 829 830 bool hasStateObject; 831 if (!decoder.decodeBool(hasStateObject)) 832 return 0; 833 if (hasStateObject) { 834#if !USE(V8) 835 Vector<uint8_t> bytes; 836 if (!decoder.decodeBytes(bytes)) 837 return 0; 838 node->m_stateObject = SerializedScriptValue::adopt(bytes); 839#else 840 String string; 841 if (!decoder.decodeString(string)) 842 return 0; 843 node->m_stateObject = SerializedScriptValue::createFromWire(string); 844#endif 845 } 846 847 if (!decoder.decodeString(node->m_target)) 848 return 0; 849 850 // Simulate recursion with our own stack. 851 if (!recursionStack.isEmpty()) { 852 DecodeRecursionStackElement& element = recursionStack.last(); 853 child = node.release(); 854 node = element.node.release(); 855 i = element.i; 856 size = element.size; 857 recursionStack.removeLast(); 858 goto resume; 859 } 860 861 return node.release(); 862} 863 864#ifndef NDEBUG 865 866int HistoryItem::showTree() const 867{ 868 return showTreeWithIndent(0); 869} 870 871int HistoryItem::showTreeWithIndent(unsigned indentLevel) const 872{ 873 Vector<char> prefix; 874 for (unsigned i = 0; i < indentLevel; ++i) 875 prefix.append(" ", 2); 876 prefix.append("\0", 1); 877 878 fprintf(stderr, "%s+-%s (%p)\n", prefix.data(), m_urlString.utf8().data(), this); 879 880 int totalSubItems = 0; 881 for (unsigned i = 0; i < m_children.size(); ++i) 882 totalSubItems += m_children[i]->showTreeWithIndent(indentLevel + 1); 883 return totalSubItems + 1; 884} 885 886#endif 887 888} // namespace WebCore 889 890#ifndef NDEBUG 891 892int showTree(const WebCore::HistoryItem* item) 893{ 894 return item->showTree(); 895} 896 897#endif 898