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