Home | History | Annotate | Download | only in history
      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