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 42 namespace WebCore { 43 44 const uint32_t backForwardTreeEncodingVersion = 2; 45 46 static 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 54 static void defaultNotifyHistoryItemChanged(HistoryItem*) 55 { 56 } 57 58 void (*notifyHistoryItemChanged)(HistoryItem*) = defaultNotifyHistoryItemChanged; 59 60 HistoryItem::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 74 HistoryItem::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 92 HistoryItem::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 111 HistoryItem::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 131 HistoryItem::~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 141 inline 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 175 PassRefPtr<HistoryItem> HistoryItem::copy() const 176 { 177 return adoptRef(new HistoryItem(*this)); 178 } 179 180 void 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 212 const 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. 219 const String& HistoryItem::originalURLString() const 220 { 221 return m_originalURLString; 222 } 223 224 const String& HistoryItem::title() const 225 { 226 return m_title; 227 } 228 229 const String& HistoryItem::alternateTitle() const 230 { 231 return m_displayTitle; 232 } 233 234 double HistoryItem::lastVisitedTime() const 235 { 236 return m_lastVisitedTime; 237 } 238 239 KURL HistoryItem::url() const 240 { 241 return KURL(ParsedURLString, m_urlString); 242 } 243 244 KURL HistoryItem::originalURL() const 245 { 246 return KURL(ParsedURLString, m_originalURLString); 247 } 248 249 const String& HistoryItem::referrer() const 250 { 251 return m_referrer; 252 } 253 254 const String& HistoryItem::target() const 255 { 256 return m_target; 257 } 258 259 const String& HistoryItem::parent() const 260 { 261 return m_parent; 262 } 263 264 void HistoryItem::setAlternateTitle(const String& alternateTitle) 265 { 266 m_displayTitle = alternateTitle; 267 notifyHistoryItemChanged(this); 268 } 269 270 void 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 281 void HistoryItem::setURL(const KURL& url) 282 { 283 pageCache()->remove(this); 284 setURLString(url.string()); 285 clearDocumentState(); 286 } 287 288 void HistoryItem::setOriginalURLString(const String& urlString) 289 { 290 m_originalURLString = urlString; 291 notifyHistoryItemChanged(this); 292 } 293 294 void HistoryItem::setReferrer(const String& referrer) 295 { 296 m_referrer = referrer; 297 notifyHistoryItemChanged(this); 298 } 299 300 void HistoryItem::setTitle(const String& title) 301 { 302 m_title = title; 303 notifyHistoryItemChanged(this); 304 } 305 306 void HistoryItem::setTarget(const String& target) 307 { 308 m_target = target; 309 notifyHistoryItemChanged(this); 310 } 311 312 void HistoryItem::setParent(const String& parent) 313 { 314 m_parent = parent; 315 } 316 317 static inline int timeToDay(double time) 318 { 319 static const double secondsPerDay = 60 * 60 * 24; 320 return static_cast<int>(ceil(time / secondsPerDay)); 321 } 322 323 void 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 338 static const size_t daysPerWeek = 7; 339 static const size_t maxDailyCounts = 2 * daysPerWeek - 1; 340 static const size_t maxWeeklyCounts = 5; 341 342 void 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 356 void 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 370 void HistoryItem::setLastVisitedTime(double time) 371 { 372 if (m_lastVisitedTime != time) 373 recordVisitAtTime(time); 374 } 375 376 void HistoryItem::visited(const String& title, double time, VisitCountBehavior visitCountBehavior) 377 { 378 m_title = title; 379 recordVisitAtTime(time, visitCountBehavior); 380 } 381 382 int HistoryItem::visitCount() const 383 { 384 return m_visitCount; 385 } 386 387 void HistoryItem::recordInitialVisit() 388 { 389 ASSERT(!m_visitCount); 390 recordVisitAtTime(m_lastVisitedTime); 391 } 392 393 void HistoryItem::setVisitCount(int count) 394 { 395 m_visitCount = count; 396 } 397 398 void 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 406 const IntPoint& HistoryItem::scrollPoint() const 407 { 408 return m_scrollPoint; 409 } 410 411 void HistoryItem::setScrollPoint(const IntPoint& point) 412 { 413 m_scrollPoint = point; 414 } 415 416 void HistoryItem::clearScrollPoint() 417 { 418 m_scrollPoint.setX(0); 419 m_scrollPoint.setY(0); 420 } 421 422 float HistoryItem::pageScaleFactor() const 423 { 424 return m_pageScaleFactor; 425 } 426 427 void HistoryItem::setPageScaleFactor(float scaleFactor) 428 { 429 m_pageScaleFactor = scaleFactor; 430 } 431 432 void HistoryItem::setDocumentState(const Vector<String>& state) 433 { 434 m_documentState = state; 435 #if PLATFORM(ANDROID) 436 notifyHistoryItemChanged(this); 437 #endif 438 } 439 440 const Vector<String>& HistoryItem::documentState() const 441 { 442 return m_documentState; 443 } 444 445 void HistoryItem::clearDocumentState() 446 { 447 m_documentState.clear(); 448 #if PLATFORM(ANDROID) 449 notifyHistoryItemChanged(this); 450 #endif 451 } 452 453 bool HistoryItem::isTargetItem() const 454 { 455 return m_isTargetItem; 456 } 457 458 void HistoryItem::setIsTargetItem(bool flag) 459 { 460 m_isTargetItem = flag; 461 #if PLATFORM(ANDROID) 462 notifyHistoryItemChanged(this); 463 #endif 464 } 465 466 void HistoryItem::setStateObject(PassRefPtr<SerializedScriptValue> object) 467 { 468 m_stateObject = object; 469 } 470 471 void 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 480 void 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 494 HistoryItem* 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 504 HistoryItem* 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. 515 HistoryItem* 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 527 HistoryItem* HistoryItem::targetItem() 528 { 529 HistoryItem* foundItem = findTargetItem(); 530 return foundItem ? foundItem : this; 531 } 532 533 const HistoryItemVector& HistoryItem::children() const 534 { 535 return m_children; 536 } 537 538 bool HistoryItem::hasChildren() const 539 { 540 return !m_children.isEmpty(); 541 } 542 543 void 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) 551 bool 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. 567 bool 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. 587 bool 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 603 String HistoryItem::formContentType() const 604 { 605 return m_formContentType; 606 } 607 608 void 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 626 void HistoryItem::setFormData(PassRefPtr<FormData> formData) 627 { 628 m_formData = formData; 629 } 630 631 void HistoryItem::setFormContentType(const String& formContentType) 632 { 633 m_formContentType = formContentType; 634 } 635 636 FormData* HistoryItem::formData() 637 { 638 return m_formData.get(); 639 } 640 641 bool 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 647 void 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 657 void 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 668 Vector<String>* HistoryItem::redirectURLs() const 669 { 670 return m_redirectURLs.get(); 671 } 672 673 void HistoryItem::setRedirectURLs(PassOwnPtr<Vector<String> > redirectURLs) 674 { 675 m_redirectURLs = redirectURLs; 676 } 677 678 void HistoryItem::encodeBackForwardTree(Encoder& encoder) const 679 { 680 encoder.encodeUInt32(backForwardTreeEncodingVersion); 681 682 encodeBackForwardTreeNode(encoder); 683 } 684 685 void 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 733 struct 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 746 PassRefPtr<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 763 recurse: 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 785 resume: 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 866 int HistoryItem::showTree() const 867 { 868 return showTreeWithIndent(0); 869 } 870 871 int 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 892 int showTree(const WebCore::HistoryItem* item) 893 { 894 return item->showTree(); 895 } 896 897 #endif 898