• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2005, 2006, 2008 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 "CString.h"
30 #include "CachedPage.h"
31 #include "Document.h"
32 #include "IconDatabase.h"
33 #include "PageCache.h"
34 #include "ResourceRequest.h"
35 #include <stdio.h>
36 #include <wtf/CurrentTime.h>
37 
38 namespace WebCore {
39 
generateDocumentSequenceNumber()40 static long long generateDocumentSequenceNumber()
41 {
42     // Initialize to the current time to reduce the likelihood of generating
43     // identifiers that overlap with those from past/future browser sessions.
44     static long long next = static_cast<long long>(currentTime() * 1000000.0);
45     return ++next;
46 }
47 
defaultNotifyHistoryItemChanged(HistoryItem *)48 static void defaultNotifyHistoryItemChanged(HistoryItem*)
49 {
50 }
51 
52 void (*notifyHistoryItemChanged)(HistoryItem*) = defaultNotifyHistoryItemChanged;
53 
HistoryItem()54 HistoryItem::HistoryItem()
55     : m_lastVisitedTime(0)
56     , m_lastVisitWasHTTPNonGet(false)
57     , m_lastVisitWasFailure(false)
58     , m_isTargetItem(false)
59     , m_visitCount(0)
60     , m_documentSequenceNumber(generateDocumentSequenceNumber())
61 {
62 }
63 
HistoryItem(const String & urlString,const String & title,double time)64 HistoryItem::HistoryItem(const String& urlString, const String& title, double time)
65     : m_urlString(urlString)
66     , m_originalURLString(urlString)
67     , m_title(title)
68     , m_lastVisitedTime(time)
69     , m_lastVisitWasHTTPNonGet(false)
70     , m_lastVisitWasFailure(false)
71     , m_isTargetItem(false)
72     , m_visitCount(0)
73     , m_documentSequenceNumber(generateDocumentSequenceNumber())
74 {
75     iconDatabase()->retainIconForPageURL(m_urlString);
76 }
77 
HistoryItem(const String & urlString,const String & title,const String & alternateTitle,double time)78 HistoryItem::HistoryItem(const String& urlString, const String& title, const String& alternateTitle, double time)
79     : m_urlString(urlString)
80     , m_originalURLString(urlString)
81     , m_title(title)
82     , m_displayTitle(alternateTitle)
83     , m_lastVisitedTime(time)
84     , m_lastVisitWasHTTPNonGet(false)
85     , m_lastVisitWasFailure(false)
86     , m_isTargetItem(false)
87     , m_visitCount(0)
88     , m_documentSequenceNumber(generateDocumentSequenceNumber())
89 {
90     iconDatabase()->retainIconForPageURL(m_urlString);
91 }
92 
HistoryItem(const KURL & url,const String & target,const String & parent,const String & title)93 HistoryItem::HistoryItem(const KURL& url, const String& target, const String& parent, const String& title)
94     : m_urlString(url.string())
95     , m_originalURLString(url.string())
96     , m_target(target)
97     , m_parent(parent)
98     , m_title(title)
99     , m_lastVisitedTime(0)
100     , m_lastVisitWasHTTPNonGet(false)
101     , m_lastVisitWasFailure(false)
102     , m_isTargetItem(false)
103     , m_visitCount(0)
104     , m_documentSequenceNumber(generateDocumentSequenceNumber())
105 {
106     iconDatabase()->retainIconForPageURL(m_urlString);
107 }
108 
~HistoryItem()109 HistoryItem::~HistoryItem()
110 {
111     ASSERT(!m_cachedPage);
112     iconDatabase()->releaseIconForPageURL(m_urlString);
113 #if PLATFORM(ANDROID)
114     if (m_bridge)
115         m_bridge->detachHistoryItem();
116 #endif
117 }
118 
HistoryItem(const HistoryItem & item)119 inline HistoryItem::HistoryItem(const HistoryItem& item)
120     : RefCounted<HistoryItem>()
121     , m_urlString(item.m_urlString)
122     , m_originalURLString(item.m_originalURLString)
123     , m_referrer(item.m_referrer)
124     , m_target(item.m_target)
125     , m_parent(item.m_parent)
126     , m_title(item.m_title)
127     , m_displayTitle(item.m_displayTitle)
128     , m_lastVisitedTime(item.m_lastVisitedTime)
129     , m_lastVisitWasHTTPNonGet(item.m_lastVisitWasHTTPNonGet)
130     , m_scrollPoint(item.m_scrollPoint)
131     , m_lastVisitWasFailure(item.m_lastVisitWasFailure)
132     , m_isTargetItem(item.m_isTargetItem)
133     , m_visitCount(item.m_visitCount)
134     , m_dailyVisitCounts(item.m_dailyVisitCounts)
135     , m_weeklyVisitCounts(item.m_weeklyVisitCounts)
136     , m_documentSequenceNumber(generateDocumentSequenceNumber())
137     , m_formContentType(item.m_formContentType)
138 {
139     if (item.m_formData)
140         m_formData = item.m_formData->copy();
141 
142     unsigned size = item.m_children.size();
143     m_children.reserveInitialCapacity(size);
144     for (unsigned i = 0; i < size; ++i)
145         m_children.uncheckedAppend(item.m_children[i]->copy());
146 
147     if (item.m_redirectURLs)
148         m_redirectURLs.set(new Vector<String>(*item.m_redirectURLs));
149 }
150 
copy() const151 PassRefPtr<HistoryItem> HistoryItem::copy() const
152 {
153     return adoptRef(new HistoryItem(*this));
154 }
155 
urlString() const156 const String& HistoryItem::urlString() const
157 {
158     return m_urlString;
159 }
160 
161 // The first URL we loaded to get to where this history item points.  Includes both client
162 // and server redirects.
originalURLString() const163 const String& HistoryItem::originalURLString() const
164 {
165     return m_originalURLString;
166 }
167 
title() const168 const String& HistoryItem::title() const
169 {
170     return m_title;
171 }
172 
alternateTitle() const173 const String& HistoryItem::alternateTitle() const
174 {
175     return m_displayTitle;
176 }
177 
icon() const178 Image* HistoryItem::icon() const
179 {
180     Image* result = iconDatabase()->iconForPageURL(m_urlString, IntSize(16, 16));
181     return result ? result : iconDatabase()->defaultIcon(IntSize(16, 16));
182 }
183 
lastVisitedTime() const184 double HistoryItem::lastVisitedTime() const
185 {
186     return m_lastVisitedTime;
187 }
188 
url() const189 KURL HistoryItem::url() const
190 {
191     return KURL(ParsedURLString, m_urlString);
192 }
193 
originalURL() const194 KURL HistoryItem::originalURL() const
195 {
196     return KURL(ParsedURLString, m_originalURLString);
197 }
198 
referrer() const199 const String& HistoryItem::referrer() const
200 {
201     return m_referrer;
202 }
203 
target() const204 const String& HistoryItem::target() const
205 {
206     return m_target;
207 }
208 
parent() const209 const String& HistoryItem::parent() const
210 {
211     return m_parent;
212 }
213 
setAlternateTitle(const String & alternateTitle)214 void HistoryItem::setAlternateTitle(const String& alternateTitle)
215 {
216     m_displayTitle = alternateTitle;
217     notifyHistoryItemChanged(this);
218 }
219 
setURLString(const String & urlString)220 void HistoryItem::setURLString(const String& urlString)
221 {
222     if (m_urlString != urlString) {
223         iconDatabase()->releaseIconForPageURL(m_urlString);
224         m_urlString = urlString;
225         iconDatabase()->retainIconForPageURL(m_urlString);
226     }
227 
228     notifyHistoryItemChanged(this);
229 }
230 
setURL(const KURL & url)231 void HistoryItem::setURL(const KURL& url)
232 {
233     pageCache()->remove(this);
234     setURLString(url.string());
235     clearDocumentState();
236 }
237 
setOriginalURLString(const String & urlString)238 void HistoryItem::setOriginalURLString(const String& urlString)
239 {
240     m_originalURLString = urlString;
241     notifyHistoryItemChanged(this);
242 }
243 
setReferrer(const String & referrer)244 void HistoryItem::setReferrer(const String& referrer)
245 {
246     m_referrer = referrer;
247     notifyHistoryItemChanged(this);
248 }
249 
setTitle(const String & title)250 void HistoryItem::setTitle(const String& title)
251 {
252     m_title = title;
253     notifyHistoryItemChanged(this);
254 }
255 
setTarget(const String & target)256 void HistoryItem::setTarget(const String& target)
257 {
258     m_target = target;
259     notifyHistoryItemChanged(this);
260 }
261 
setParent(const String & parent)262 void HistoryItem::setParent(const String& parent)
263 {
264     m_parent = parent;
265 }
266 
timeToDay(double time)267 static inline int timeToDay(double time)
268 {
269     static const double secondsPerDay = 60 * 60 * 24;
270     return static_cast<int>(ceil(time / secondsPerDay));
271 }
272 
padDailyCountsForNewVisit(double time)273 void HistoryItem::padDailyCountsForNewVisit(double time)
274 {
275     if (m_dailyVisitCounts.isEmpty())
276         m_dailyVisitCounts.prepend(m_visitCount);
277 
278     int daysElapsed = timeToDay(time) - timeToDay(m_lastVisitedTime);
279 
280     if (daysElapsed < 0)
281       daysElapsed = 0;
282 
283     Vector<int> padding;
284     padding.fill(0, daysElapsed);
285     m_dailyVisitCounts.prepend(padding);
286 }
287 
288 static const size_t daysPerWeek = 7;
289 static const size_t maxDailyCounts = 2 * daysPerWeek - 1;
290 static const size_t maxWeeklyCounts = 5;
291 
collapseDailyVisitsToWeekly()292 void HistoryItem::collapseDailyVisitsToWeekly()
293 {
294     while (m_dailyVisitCounts.size() > maxDailyCounts) {
295         int oldestWeekTotal = 0;
296         for (size_t i = 0; i < daysPerWeek; i++)
297             oldestWeekTotal += m_dailyVisitCounts[m_dailyVisitCounts.size() - daysPerWeek + i];
298         m_dailyVisitCounts.shrink(m_dailyVisitCounts.size() - daysPerWeek);
299         m_weeklyVisitCounts.prepend(oldestWeekTotal);
300     }
301 
302     if (m_weeklyVisitCounts.size() > maxWeeklyCounts)
303         m_weeklyVisitCounts.shrink(maxWeeklyCounts);
304 }
305 
recordVisitAtTime(double time,VisitCountBehavior visitCountBehavior)306 void HistoryItem::recordVisitAtTime(double time, VisitCountBehavior visitCountBehavior)
307 {
308     padDailyCountsForNewVisit(time);
309 
310     m_lastVisitedTime = time;
311 
312     if (visitCountBehavior == IncreaseVisitCount) {
313         ++m_visitCount;
314         ++m_dailyVisitCounts[0];
315     }
316 
317     collapseDailyVisitsToWeekly();
318 }
319 
setLastVisitedTime(double time)320 void HistoryItem::setLastVisitedTime(double time)
321 {
322     if (m_lastVisitedTime != time)
323         recordVisitAtTime(time);
324 }
325 
visited(const String & title,double time,VisitCountBehavior visitCountBehavior)326 void HistoryItem::visited(const String& title, double time, VisitCountBehavior visitCountBehavior)
327 {
328     m_title = title;
329     recordVisitAtTime(time, visitCountBehavior);
330 }
331 
visitCount() const332 int HistoryItem::visitCount() const
333 {
334     return m_visitCount;
335 }
336 
recordInitialVisit()337 void HistoryItem::recordInitialVisit()
338 {
339     ASSERT(!m_visitCount);
340     recordVisitAtTime(m_lastVisitedTime);
341 }
342 
setVisitCount(int count)343 void HistoryItem::setVisitCount(int count)
344 {
345     m_visitCount = count;
346 }
347 
adoptVisitCounts(Vector<int> & dailyCounts,Vector<int> & weeklyCounts)348 void HistoryItem::adoptVisitCounts(Vector<int>& dailyCounts, Vector<int>& weeklyCounts)
349 {
350     m_dailyVisitCounts.clear();
351     m_dailyVisitCounts.swap(dailyCounts);
352     m_weeklyVisitCounts.clear();
353     m_weeklyVisitCounts.swap(weeklyCounts);
354 }
355 
scrollPoint() const356 const IntPoint& HistoryItem::scrollPoint() const
357 {
358     return m_scrollPoint;
359 }
360 
setScrollPoint(const IntPoint & point)361 void HistoryItem::setScrollPoint(const IntPoint& point)
362 {
363     m_scrollPoint = point;
364 }
365 
clearScrollPoint()366 void HistoryItem::clearScrollPoint()
367 {
368     m_scrollPoint.setX(0);
369     m_scrollPoint.setY(0);
370 }
371 
setDocumentState(const Vector<String> & state)372 void HistoryItem::setDocumentState(const Vector<String>& state)
373 {
374     m_documentState = state;
375 #if PLATFORM(ANDROID)
376     notifyHistoryItemChanged(this);
377 #endif
378 }
379 
documentState() const380 const Vector<String>& HistoryItem::documentState() const
381 {
382     return m_documentState;
383 }
384 
clearDocumentState()385 void HistoryItem::clearDocumentState()
386 {
387     m_documentState.clear();
388 #if PLATFORM(ANDROID)
389     notifyHistoryItemChanged(this);
390 #endif
391 }
392 
isTargetItem() const393 bool HistoryItem::isTargetItem() const
394 {
395     return m_isTargetItem;
396 }
397 
setIsTargetItem(bool flag)398 void HistoryItem::setIsTargetItem(bool flag)
399 {
400     m_isTargetItem = flag;
401 #if PLATFORM(ANDROID)
402     notifyHistoryItemChanged(this);
403 #endif
404 }
405 
setStateObject(PassRefPtr<SerializedScriptValue> object)406 void HistoryItem::setStateObject(PassRefPtr<SerializedScriptValue> object)
407 {
408     m_stateObject = object;
409 }
410 
addChildItem(PassRefPtr<HistoryItem> child)411 void HistoryItem::addChildItem(PassRefPtr<HistoryItem> child)
412 {
413     ASSERT(!childItemWithTarget(child->target()));
414     m_children.append(child);
415 #if PLATFORM(ANDROID)
416     notifyHistoryItemChanged(this);
417 #endif
418 }
419 
setChildItem(PassRefPtr<HistoryItem> child)420 void HistoryItem::setChildItem(PassRefPtr<HistoryItem> child)
421 {
422     ASSERT(!child->isTargetItem());
423     unsigned size = m_children.size();
424     for (unsigned i = 0; i < size; ++i)  {
425         if (m_children[i]->target() == child->target()) {
426             child->setIsTargetItem(m_children[i]->isTargetItem());
427             m_children[i] = child;
428             return;
429         }
430     }
431     m_children.append(child);
432 }
433 
childItemWithTarget(const String & target) const434 HistoryItem* HistoryItem::childItemWithTarget(const String& target) const
435 {
436     unsigned size = m_children.size();
437     for (unsigned i = 0; i < size; ++i) {
438         if (m_children[i]->target() == target)
439             return m_children[i].get();
440     }
441     return 0;
442 }
443 
444 // <rdar://problem/4895849> HistoryItem::findTargetItem() should be replaced with a non-recursive method.
findTargetItem()445 HistoryItem* HistoryItem::findTargetItem()
446 {
447     if (m_isTargetItem)
448         return this;
449     unsigned size = m_children.size();
450     for (unsigned i = 0; i < size; ++i) {
451         if (HistoryItem* match = m_children[i]->targetItem())
452             return match;
453     }
454     return 0;
455 }
456 
targetItem()457 HistoryItem* HistoryItem::targetItem()
458 {
459     HistoryItem* foundItem = findTargetItem();
460     return foundItem ? foundItem : this;
461 }
462 
children() const463 const HistoryItemVector& HistoryItem::children() const
464 {
465     return m_children;
466 }
467 
hasChildren() const468 bool HistoryItem::hasChildren() const
469 {
470     return !m_children.isEmpty();
471 }
472 
clearChildren()473 void HistoryItem::clearChildren()
474 {
475     m_children.clear();
476 }
477 
formContentType() const478 String HistoryItem::formContentType() const
479 {
480     return m_formContentType;
481 }
482 
setFormInfoFromRequest(const ResourceRequest & request)483 void HistoryItem::setFormInfoFromRequest(const ResourceRequest& request)
484 {
485     m_referrer = request.httpReferrer();
486 
487     if (equalIgnoringCase(request.httpMethod(), "POST")) {
488         // FIXME: Eventually we have to make this smart enough to handle the case where
489         // we have a stream for the body to handle the "data interspersed with files" feature.
490         m_formData = request.httpBody();
491         m_formContentType = request.httpContentType();
492     } else {
493         m_formData = 0;
494         m_formContentType = String();
495     }
496 #if PLATFORM(ANDROID)
497     notifyHistoryItemChanged(this);
498 #endif
499 }
500 
setFormData(PassRefPtr<FormData> formData)501 void HistoryItem::setFormData(PassRefPtr<FormData> formData)
502 {
503     m_formData = formData;
504 }
505 
setFormContentType(const String & formContentType)506 void HistoryItem::setFormContentType(const String& formContentType)
507 {
508     m_formContentType = formContentType;
509 }
510 
formData()511 FormData* HistoryItem::formData()
512 {
513     return m_formData.get();
514 }
515 
isCurrentDocument(Document * doc) const516 bool HistoryItem::isCurrentDocument(Document* doc) const
517 {
518     // FIXME: We should find a better way to check if this is the current document.
519     return equalIgnoringFragmentIdentifier(url(), doc->url());
520 }
521 
mergeAutoCompleteHints(HistoryItem * otherItem)522 void HistoryItem::mergeAutoCompleteHints(HistoryItem* otherItem)
523 {
524     // FIXME: this is broken - we should be merging the daily counts
525     // somehow.  but this is to support API that's not really used in
526     // practice so leave it broken for now.
527     ASSERT(otherItem);
528     if (otherItem != this)
529         m_visitCount += otherItem->m_visitCount;
530 }
531 
addRedirectURL(const String & url)532 void HistoryItem::addRedirectURL(const String& url)
533 {
534     if (!m_redirectURLs)
535         m_redirectURLs.set(new Vector<String>);
536 
537     // Our API allows us to store all the URLs in the redirect chain, but for
538     // now we only have a use for the final URL.
539     (*m_redirectURLs).resize(1);
540     (*m_redirectURLs)[0] = url;
541 }
542 
redirectURLs() const543 Vector<String>* HistoryItem::redirectURLs() const
544 {
545     return m_redirectURLs.get();
546 }
547 
setRedirectURLs(PassOwnPtr<Vector<String>> redirectURLs)548 void HistoryItem::setRedirectURLs(PassOwnPtr<Vector<String> > redirectURLs)
549 {
550     m_redirectURLs = redirectURLs;
551 }
552 
553 #ifndef NDEBUG
554 
showTree() const555 int HistoryItem::showTree() const
556 {
557     return showTreeWithIndent(0);
558 }
559 
showTreeWithIndent(unsigned indentLevel) const560 int HistoryItem::showTreeWithIndent(unsigned indentLevel) const
561 {
562     Vector<char> prefix;
563     for (unsigned i = 0; i < indentLevel; ++i)
564         prefix.append("  ", 2);
565     prefix.append("\0", 1);
566 
567     fprintf(stderr, "%s+-%s (%p)\n", prefix.data(), m_urlString.utf8().data(), this);
568 
569     int totalSubItems = 0;
570     for (unsigned i = 0; i < m_children.size(); ++i)
571         totalSubItems += m_children[i]->showTreeWithIndent(indentLevel + 1);
572     return totalSubItems + 1;
573 }
574 
575 #endif
576 
577 } // namespace WebCore
578 
579 #ifndef NDEBUG
580 
showTree(const WebCore::HistoryItem * item)581 int showTree(const WebCore::HistoryItem* item)
582 {
583     return item->showTree();
584 }
585 
586 #endif
587