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