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