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