• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
generateSequenceNumber()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 
defaultNotifyHistoryItemChanged(HistoryItem *)54 static void defaultNotifyHistoryItemChanged(HistoryItem*)
55 {
56 }
57 
58 void (*notifyHistoryItemChanged)(HistoryItem*) = defaultNotifyHistoryItemChanged;
59 
HistoryItem()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 
HistoryItem(const String & urlString,const String & title,double time)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 
HistoryItem(const String & urlString,const String & title,const String & alternateTitle,double time)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 
HistoryItem(const KURL & url,const String & target,const String & parent,const String & title)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 
~HistoryItem()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 
HistoryItem(const HistoryItem & item)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 
copy() const175 PassRefPtr<HistoryItem> HistoryItem::copy() const
176 {
177     return adoptRef(new HistoryItem(*this));
178 }
179 
reset()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 
urlString() const212 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.
originalURLString() const219 const String& HistoryItem::originalURLString() const
220 {
221     return m_originalURLString;
222 }
223 
title() const224 const String& HistoryItem::title() const
225 {
226     return m_title;
227 }
228 
alternateTitle() const229 const String& HistoryItem::alternateTitle() const
230 {
231     return m_displayTitle;
232 }
233 
lastVisitedTime() const234 double HistoryItem::lastVisitedTime() const
235 {
236     return m_lastVisitedTime;
237 }
238 
url() const239 KURL HistoryItem::url() const
240 {
241     return KURL(ParsedURLString, m_urlString);
242 }
243 
originalURL() const244 KURL HistoryItem::originalURL() const
245 {
246     return KURL(ParsedURLString, m_originalURLString);
247 }
248 
referrer() const249 const String& HistoryItem::referrer() const
250 {
251     return m_referrer;
252 }
253 
target() const254 const String& HistoryItem::target() const
255 {
256     return m_target;
257 }
258 
parent() const259 const String& HistoryItem::parent() const
260 {
261     return m_parent;
262 }
263 
setAlternateTitle(const String & alternateTitle)264 void HistoryItem::setAlternateTitle(const String& alternateTitle)
265 {
266     m_displayTitle = alternateTitle;
267     notifyHistoryItemChanged(this);
268 }
269 
setURLString(const String & urlString)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 
setURL(const KURL & url)281 void HistoryItem::setURL(const KURL& url)
282 {
283     pageCache()->remove(this);
284     setURLString(url.string());
285     clearDocumentState();
286 }
287 
setOriginalURLString(const String & urlString)288 void HistoryItem::setOriginalURLString(const String& urlString)
289 {
290     m_originalURLString = urlString;
291     notifyHistoryItemChanged(this);
292 }
293 
setReferrer(const String & referrer)294 void HistoryItem::setReferrer(const String& referrer)
295 {
296     m_referrer = referrer;
297     notifyHistoryItemChanged(this);
298 }
299 
setTitle(const String & title)300 void HistoryItem::setTitle(const String& title)
301 {
302     m_title = title;
303     notifyHistoryItemChanged(this);
304 }
305 
setTarget(const String & target)306 void HistoryItem::setTarget(const String& target)
307 {
308     m_target = target;
309     notifyHistoryItemChanged(this);
310 }
311 
setParent(const String & parent)312 void HistoryItem::setParent(const String& parent)
313 {
314     m_parent = parent;
315 }
316 
timeToDay(double time)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 
padDailyCountsForNewVisit(double time)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 
collapseDailyVisitsToWeekly()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 
recordVisitAtTime(double time,VisitCountBehavior visitCountBehavior)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 
setLastVisitedTime(double time)370 void HistoryItem::setLastVisitedTime(double time)
371 {
372     if (m_lastVisitedTime != time)
373         recordVisitAtTime(time);
374 }
375 
visited(const String & title,double time,VisitCountBehavior visitCountBehavior)376 void HistoryItem::visited(const String& title, double time, VisitCountBehavior visitCountBehavior)
377 {
378     m_title = title;
379     recordVisitAtTime(time, visitCountBehavior);
380 }
381 
visitCount() const382 int HistoryItem::visitCount() const
383 {
384     return m_visitCount;
385 }
386 
recordInitialVisit()387 void HistoryItem::recordInitialVisit()
388 {
389     ASSERT(!m_visitCount);
390     recordVisitAtTime(m_lastVisitedTime);
391 }
392 
setVisitCount(int count)393 void HistoryItem::setVisitCount(int count)
394 {
395     m_visitCount = count;
396 }
397 
adoptVisitCounts(Vector<int> & dailyCounts,Vector<int> & weeklyCounts)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 
scrollPoint() const406 const IntPoint& HistoryItem::scrollPoint() const
407 {
408     return m_scrollPoint;
409 }
410 
setScrollPoint(const IntPoint & point)411 void HistoryItem::setScrollPoint(const IntPoint& point)
412 {
413     m_scrollPoint = point;
414 }
415 
clearScrollPoint()416 void HistoryItem::clearScrollPoint()
417 {
418     m_scrollPoint.setX(0);
419     m_scrollPoint.setY(0);
420 }
421 
pageScaleFactor() const422 float HistoryItem::pageScaleFactor() const
423 {
424     return m_pageScaleFactor;
425 }
426 
setPageScaleFactor(float scaleFactor)427 void HistoryItem::setPageScaleFactor(float scaleFactor)
428 {
429     m_pageScaleFactor = scaleFactor;
430 }
431 
setDocumentState(const Vector<String> & state)432 void HistoryItem::setDocumentState(const Vector<String>& state)
433 {
434     m_documentState = state;
435 #if PLATFORM(ANDROID)
436     notifyHistoryItemChanged(this);
437 #endif
438 }
439 
documentState() const440 const Vector<String>& HistoryItem::documentState() const
441 {
442     return m_documentState;
443 }
444 
clearDocumentState()445 void HistoryItem::clearDocumentState()
446 {
447     m_documentState.clear();
448 #if PLATFORM(ANDROID)
449     notifyHistoryItemChanged(this);
450 #endif
451 }
452 
isTargetItem() const453 bool HistoryItem::isTargetItem() const
454 {
455     return m_isTargetItem;
456 }
457 
setIsTargetItem(bool flag)458 void HistoryItem::setIsTargetItem(bool flag)
459 {
460     m_isTargetItem = flag;
461 #if PLATFORM(ANDROID)
462     notifyHistoryItemChanged(this);
463 #endif
464 }
465 
setStateObject(PassRefPtr<SerializedScriptValue> object)466 void HistoryItem::setStateObject(PassRefPtr<SerializedScriptValue> object)
467 {
468     m_stateObject = object;
469 }
470 
addChildItem(PassRefPtr<HistoryItem> child)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 
setChildItem(PassRefPtr<HistoryItem> child)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 
childItemWithTarget(const String & target) const494 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 
childItemWithDocumentSequenceNumber(long long number) const504 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.
findTargetItem()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 
targetItem()527 HistoryItem* HistoryItem::targetItem()
528 {
529     HistoryItem* foundItem = findTargetItem();
530     return foundItem ? foundItem : this;
531 }
532 
children() const533 const HistoryItemVector& HistoryItem::children() const
534 {
535     return m_children;
536 }
537 
hasChildren() const538 bool HistoryItem::hasChildren() const
539 {
540     return !m_children.isEmpty();
541 }
542 
clearChildren()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)
shouldDoSameDocumentNavigationTo(HistoryItem * otherItem) const551 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.
hasSameDocumentTree(HistoryItem * otherItem) const567 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.
hasSameFrames(HistoryItem * otherItem) const587 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 
formContentType() const603 String HistoryItem::formContentType() const
604 {
605     return m_formContentType;
606 }
607 
setFormInfoFromRequest(const ResourceRequest & request)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 
setFormData(PassRefPtr<FormData> formData)626 void HistoryItem::setFormData(PassRefPtr<FormData> formData)
627 {
628     m_formData = formData;
629 }
630 
setFormContentType(const String & formContentType)631 void HistoryItem::setFormContentType(const String& formContentType)
632 {
633     m_formContentType = formContentType;
634 }
635 
formData()636 FormData* HistoryItem::formData()
637 {
638     return m_formData.get();
639 }
640 
isCurrentDocument(Document * doc) const641 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 
mergeAutoCompleteHints(HistoryItem * otherItem)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 
addRedirectURL(const String & url)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 
redirectURLs() const668 Vector<String>* HistoryItem::redirectURLs() const
669 {
670     return m_redirectURLs.get();
671 }
672 
setRedirectURLs(PassOwnPtr<Vector<String>> redirectURLs)673 void HistoryItem::setRedirectURLs(PassOwnPtr<Vector<String> > redirectURLs)
674 {
675     m_redirectURLs = redirectURLs;
676 }
677 
encodeBackForwardTree(Encoder & encoder) const678 void HistoryItem::encodeBackForwardTree(Encoder& encoder) const
679 {
680     encoder.encodeUInt32(backForwardTreeEncodingVersion);
681 
682     encodeBackForwardTreeNode(encoder);
683 }
684 
encodeBackForwardTreeNode(Encoder & encoder) const685 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 
DecodeRecursionStackElementWebCore::DecodeRecursionStackElement738     DecodeRecursionStackElement(PassRefPtr<HistoryItem> node, size_t i, uint64_t size)
739         : node(node)
740         , i(i)
741         , size(size)
742     {
743     }
744 };
745 
decodeBackForwardTree(const String & topURLString,const String & topTitle,const String & topOriginalURLString,Decoder & decoder)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 
showTree() const866 int HistoryItem::showTree() const
867 {
868     return showTreeWithIndent(0);
869 }
870 
showTreeWithIndent(unsigned indentLevel) const871 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 
showTree(const WebCore::HistoryItem * item)892 int showTree(const WebCore::HistoryItem* item)
893 {
894     return item->showTree();
895 }
896 
897 #endif
898