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