• 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 
37 namespace WebCore {
38 
39 #ifdef ANDROID_HISTORY_CLIENT
40 void (*notifyHistoryItemChanged)(HistoryItem*);
41 #else
42 static void defaultNotifyHistoryItemChanged()
43 {
44 }
45 
46 void (*notifyHistoryItemChanged)() = defaultNotifyHistoryItemChanged;
47 #endif
48 
HistoryItem()49 HistoryItem::HistoryItem()
50     : m_lastVisitedTime(0)
51     , m_lastVisitWasHTTPNonGet(false)
52     , m_lastVisitWasFailure(false)
53     , m_isInPageCache(false)
54     , m_isTargetItem(false)
55     , m_visitCount(0)
56 {
57 }
58 
HistoryItem(const String & urlString,const String & title,double time)59 HistoryItem::HistoryItem(const String& urlString, const String& title, double time)
60     : m_urlString(urlString)
61     , m_originalURLString(urlString)
62     , m_title(title)
63     , m_lastVisitedTime(time)
64     , m_lastVisitWasHTTPNonGet(false)
65     , m_lastVisitWasFailure(false)
66     , m_isInPageCache(false)
67     , m_isTargetItem(false)
68     , m_visitCount(0)
69 {
70     iconDatabase()->retainIconForPageURL(m_urlString);
71 }
72 
HistoryItem(const String & urlString,const String & title,const String & alternateTitle,double time)73 HistoryItem::HistoryItem(const String& urlString, const String& title, const String& alternateTitle, double time)
74     : m_urlString(urlString)
75     , m_originalURLString(urlString)
76     , m_title(title)
77     , m_displayTitle(alternateTitle)
78     , m_lastVisitedTime(time)
79     , m_lastVisitWasHTTPNonGet(false)
80     , m_lastVisitWasFailure(false)
81     , m_isInPageCache(false)
82     , m_isTargetItem(false)
83     , m_visitCount(0)
84 {
85     iconDatabase()->retainIconForPageURL(m_urlString);
86 }
87 
HistoryItem(const KURL & url,const String & target,const String & parent,const String & title)88 HistoryItem::HistoryItem(const KURL& url, const String& target, const String& parent, const String& title)
89     : m_urlString(url.string())
90     , m_originalURLString(url.string())
91     , m_target(target)
92     , m_parent(parent)
93     , m_title(title)
94     , m_lastVisitedTime(0)
95     , m_lastVisitWasHTTPNonGet(false)
96     , m_lastVisitWasFailure(false)
97     , m_isInPageCache(false)
98     , m_isTargetItem(false)
99     , m_visitCount(0)
100 {
101     iconDatabase()->retainIconForPageURL(m_urlString);
102 }
103 
~HistoryItem()104 HistoryItem::~HistoryItem()
105 {
106     ASSERT(!m_isInPageCache);
107     iconDatabase()->releaseIconForPageURL(m_urlString);
108 }
109 
HistoryItem(const HistoryItem & item)110 inline HistoryItem::HistoryItem(const HistoryItem& item)
111     : RefCounted<HistoryItem>()
112     , m_urlString(item.m_urlString)
113     , m_originalURLString(item.m_originalURLString)
114     , m_referrer(item.m_referrer)
115     , m_target(item.m_target)
116     , m_parent(item.m_parent)
117     , m_title(item.m_title)
118     , m_displayTitle(item.m_displayTitle)
119     , m_lastVisitedTime(item.m_lastVisitedTime)
120     , m_lastVisitWasHTTPNonGet(item.m_lastVisitWasHTTPNonGet)
121     , m_scrollPoint(item.m_scrollPoint)
122     , m_lastVisitWasFailure(item.m_lastVisitWasFailure)
123     , m_isInPageCache(item.m_isInPageCache)
124     , m_isTargetItem(item.m_isTargetItem)
125     , m_visitCount(item.m_visitCount)
126     , m_dailyVisitCounts(item.m_dailyVisitCounts)
127     , m_weeklyVisitCounts(item.m_weeklyVisitCounts)
128     , m_formContentType(item.m_formContentType)
129 {
130     if (item.m_formData)
131         m_formData = item.m_formData->copy();
132 
133     unsigned size = item.m_subItems.size();
134     m_subItems.reserveCapacity(size);
135     for (unsigned i = 0; i < size; ++i)
136         m_subItems.append(item.m_subItems[i]->copy());
137 
138     if (item.m_redirectURLs)
139         m_redirectURLs.set(new Vector<String>(*item.m_redirectURLs));
140 }
141 
copy() const142 PassRefPtr<HistoryItem> HistoryItem::copy() const
143 {
144     return adoptRef(new HistoryItem(*this));
145 }
146 
urlString() const147 const String& HistoryItem::urlString() const
148 {
149     return m_urlString;
150 }
151 
152 // The first URL we loaded to get to where this history item points.  Includes both client
153 // and server redirects.
originalURLString() const154 const String& HistoryItem::originalURLString() const
155 {
156     return m_originalURLString;
157 }
158 
title() const159 const String& HistoryItem::title() const
160 {
161     return m_title;
162 }
163 
alternateTitle() const164 const String& HistoryItem::alternateTitle() const
165 {
166     return m_displayTitle;
167 }
168 
icon() const169 Image* HistoryItem::icon() const
170 {
171     Image* result = iconDatabase()->iconForPageURL(m_urlString, IntSize(16,16));
172     return result ? result : iconDatabase()->defaultIcon(IntSize(16,16));
173 }
174 
lastVisitedTime() const175 double HistoryItem::lastVisitedTime() const
176 {
177     return m_lastVisitedTime;
178 }
179 
url() const180 KURL HistoryItem::url() const
181 {
182     return KURL(m_urlString);
183 }
184 
originalURL() const185 KURL HistoryItem::originalURL() const
186 {
187     return KURL(m_originalURLString);
188 }
189 
referrer() const190 const String& HistoryItem::referrer() const
191 {
192     return m_referrer;
193 }
194 
target() const195 const String& HistoryItem::target() const
196 {
197     return m_target;
198 }
199 
parent() const200 const String& HistoryItem::parent() const
201 {
202     return m_parent;
203 }
204 
setAlternateTitle(const String & alternateTitle)205 void HistoryItem::setAlternateTitle(const String& alternateTitle)
206 {
207     m_displayTitle = alternateTitle;
208 #ifndef ANDROID_HISTORY_CLIENT
209     notifyHistoryItemChanged();
210 #endif
211 }
212 
setURLString(const String & urlString)213 void HistoryItem::setURLString(const String& urlString)
214 {
215     if (m_urlString != urlString) {
216         iconDatabase()->releaseIconForPageURL(m_urlString);
217         m_urlString = urlString;
218         iconDatabase()->retainIconForPageURL(m_urlString);
219     }
220 
221 #ifdef ANDROID_HISTORY_CLIENT
222     notifyHistoryItemChanged(this);
223 #else
224     notifyHistoryItemChanged();
225 #endif
226 }
227 
setURL(const KURL & url)228 void HistoryItem::setURL(const KURL& url)
229 {
230     pageCache()->remove(this);
231     setURLString(url.string());
232     clearDocumentState();
233 }
234 
setOriginalURLString(const String & urlString)235 void HistoryItem::setOriginalURLString(const String& urlString)
236 {
237     m_originalURLString = urlString;
238 #ifdef ANDROID_HISTORY_CLIENT
239     notifyHistoryItemChanged(this);
240 #else
241     notifyHistoryItemChanged();
242 #endif
243 }
244 
setReferrer(const String & referrer)245 void HistoryItem::setReferrer(const String& referrer)
246 {
247     m_referrer = referrer;
248 #ifdef ANDROID_HISTORY_CLIENT
249     notifyHistoryItemChanged(this);
250 #else
251     notifyHistoryItemChanged();
252 #endif
253 }
254 
setTitle(const String & title)255 void HistoryItem::setTitle(const String& title)
256 {
257     m_title = title;
258 #ifdef ANDROID_HISTORY_CLIENT
259     notifyHistoryItemChanged(this);
260 #else
261     notifyHistoryItemChanged();
262 #endif
263 }
264 
setTarget(const String & target)265 void HistoryItem::setTarget(const String& target)
266 {
267     m_target = target;
268 #ifdef ANDROID_HISTORY_CLIENT
269     notifyHistoryItemChanged(this);
270 #else
271     notifyHistoryItemChanged();
272 #endif
273 }
274 
setParent(const String & parent)275 void HistoryItem::setParent(const String& parent)
276 {
277     m_parent = parent;
278 }
279 
timeToDay(double time)280 static inline int timeToDay(double time)
281 {
282     static const double secondsPerDay = 60 * 60 * 24;
283     return static_cast<int>(ceil(time / secondsPerDay));
284 }
285 
padDailyCountsForNewVisit(double time)286 void HistoryItem::padDailyCountsForNewVisit(double time)
287 {
288     if (m_dailyVisitCounts.isEmpty())
289         m_dailyVisitCounts.prepend(m_visitCount);
290 
291     int daysElapsed = timeToDay(time) - timeToDay(m_lastVisitedTime);
292 
293     if (daysElapsed < 0)
294       daysElapsed = 0;
295 
296     Vector<int> padding;
297     padding.fill(0, daysElapsed);
298     m_dailyVisitCounts.prepend(padding);
299 }
300 
301 static const size_t daysPerWeek = 7;
302 static const size_t maxDailyCounts = 2 * daysPerWeek - 1;
303 static const size_t maxWeeklyCounts = 5;
304 
collapseDailyVisitsToWeekly()305 void HistoryItem::collapseDailyVisitsToWeekly()
306 {
307     while (m_dailyVisitCounts.size() > maxDailyCounts) {
308         int oldestWeekTotal = 0;
309         for (size_t i = 0; i < daysPerWeek; i++)
310             oldestWeekTotal += m_dailyVisitCounts[m_dailyVisitCounts.size() - daysPerWeek + i];
311         m_dailyVisitCounts.shrink(m_dailyVisitCounts.size() - daysPerWeek);
312         m_weeklyVisitCounts.prepend(oldestWeekTotal);
313     }
314 
315     if (m_weeklyVisitCounts.size() > maxWeeklyCounts)
316         m_weeklyVisitCounts.shrink(maxWeeklyCounts);
317 }
318 
recordVisitAtTime(double time)319 void HistoryItem::recordVisitAtTime(double time)
320 {
321     padDailyCountsForNewVisit(time);
322 
323     m_lastVisitedTime = time;
324     m_visitCount++;
325 
326     m_dailyVisitCounts[0]++;
327 
328     collapseDailyVisitsToWeekly();
329 }
330 
setLastVisitedTime(double time)331 void HistoryItem::setLastVisitedTime(double time)
332 {
333     if (m_lastVisitedTime != time)
334         recordVisitAtTime(time);
335 }
336 
visited(const String & title,double time)337 void HistoryItem::visited(const String& title, double time)
338 {
339     m_title = title;
340     recordVisitAtTime(time);
341 }
342 
visitCount() const343 int HistoryItem::visitCount() const
344 {
345     return m_visitCount;
346 }
347 
recordInitialVisit()348 void HistoryItem::recordInitialVisit()
349 {
350     ASSERT(!m_visitCount);
351     recordVisitAtTime(m_lastVisitedTime);
352 }
353 
setVisitCount(int count)354 void HistoryItem::setVisitCount(int count)
355 {
356     m_visitCount = count;
357 }
358 
adoptVisitCounts(Vector<int> & dailyCounts,Vector<int> & weeklyCounts)359 void HistoryItem::adoptVisitCounts(Vector<int>& dailyCounts, Vector<int>& weeklyCounts)
360 {
361   m_dailyVisitCounts.clear();
362   m_dailyVisitCounts.swap(dailyCounts);
363   m_weeklyVisitCounts.clear();
364   m_weeklyVisitCounts.swap(weeklyCounts);
365 }
366 
scrollPoint() const367 const IntPoint& HistoryItem::scrollPoint() const
368 {
369     return m_scrollPoint;
370 }
371 
setScrollPoint(const IntPoint & point)372 void HistoryItem::setScrollPoint(const IntPoint& point)
373 {
374     m_scrollPoint = point;
375 }
376 
clearScrollPoint()377 void HistoryItem::clearScrollPoint()
378 {
379     m_scrollPoint.setX(0);
380     m_scrollPoint.setY(0);
381 }
382 
setDocumentState(const Vector<String> & state)383 void HistoryItem::setDocumentState(const Vector<String>& state)
384 {
385     m_documentState = state;
386 #ifdef ANDROID_HISTORY_CLIENT
387     notifyHistoryItemChanged(this);
388 #endif
389 }
390 
documentState() const391 const Vector<String>& HistoryItem::documentState() const
392 {
393     return m_documentState;
394 }
395 
clearDocumentState()396 void HistoryItem::clearDocumentState()
397 {
398     m_documentState.clear();
399 #ifdef ANDROID_HISTORY_CLIENT
400     notifyHistoryItemChanged(this);
401 #endif
402 }
403 
isTargetItem() const404 bool HistoryItem::isTargetItem() const
405 {
406     return m_isTargetItem;
407 }
408 
setIsTargetItem(bool flag)409 void HistoryItem::setIsTargetItem(bool flag)
410 {
411     m_isTargetItem = flag;
412 #ifdef ANDROID_HISTORY_CLIENT
413     notifyHistoryItemChanged(this);
414 #endif
415 }
416 
addChildItem(PassRefPtr<HistoryItem> child)417 void HistoryItem::addChildItem(PassRefPtr<HistoryItem> child)
418 {
419     m_subItems.append(child);
420 #ifdef ANDROID_HISTORY_CLIENT
421     notifyHistoryItemChanged(this);
422 #endif
423 }
424 
childItemWithName(const String & name) const425 HistoryItem* HistoryItem::childItemWithName(const String& name) const
426 {
427     unsigned size = m_subItems.size();
428     for (unsigned i = 0; i < size; ++i)
429         if (m_subItems[i]->target() == name)
430             return m_subItems[i].get();
431     return 0;
432 }
433 
434 // <rdar://problem/4895849> HistoryItem::recurseToFindTargetItem() should be replace with a non-recursive method
recurseToFindTargetItem()435 HistoryItem* HistoryItem::recurseToFindTargetItem()
436 {
437     if (m_isTargetItem)
438         return this;
439     if (!m_subItems.size())
440         return 0;
441 
442     HistoryItem* match;
443     unsigned size = m_subItems.size();
444     for (unsigned i = 0; i < size; ++i) {
445         match = m_subItems[i]->recurseToFindTargetItem();
446         if (match)
447             return match;
448     }
449 
450     return 0;
451 }
452 
targetItem()453 HistoryItem* HistoryItem::targetItem()
454 {
455     if (!m_subItems.size())
456         return this;
457     return recurseToFindTargetItem();
458 }
459 
children() const460 const HistoryItemVector& HistoryItem::children() const
461 {
462     return m_subItems;
463 }
464 
hasChildren() const465 bool HistoryItem::hasChildren() const
466 {
467     return m_subItems.size();
468 }
469 
formContentType() const470 String HistoryItem::formContentType() const
471 {
472     return m_formContentType;
473 }
474 
setFormInfoFromRequest(const ResourceRequest & request)475 void HistoryItem::setFormInfoFromRequest(const ResourceRequest& request)
476 {
477     m_referrer = request.httpReferrer();
478 
479     if (equalIgnoringCase(request.httpMethod(), "POST")) {
480         // FIXME: Eventually we have to make this smart enough to handle the case where
481         // we have a stream for the body to handle the "data interspersed with files" feature.
482         m_formData = request.httpBody();
483         m_formContentType = request.httpContentType();
484     } else {
485         m_formData = 0;
486         m_formContentType = String();
487     }
488 #ifdef ANDROID_HISTORY_CLIENT
489     notifyHistoryItemChanged(this);
490 #endif
491 }
492 
formData()493 FormData* HistoryItem::formData()
494 {
495     return m_formData.get();
496 }
497 
isCurrentDocument(Document * doc) const498 bool HistoryItem::isCurrentDocument(Document* doc) const
499 {
500     // FIXME: We should find a better way to check if this is the current document.
501     return urlString() == doc->url();
502 }
503 
mergeAutoCompleteHints(HistoryItem * otherItem)504 void HistoryItem::mergeAutoCompleteHints(HistoryItem* otherItem)
505 {
506     // FIXME: this is broken - we should be merging the daily counts
507     // somehow.  but this is to support API that's not really used in
508     // practice so leave it broken for now.
509     ASSERT(otherItem);
510     if (otherItem != this)
511         m_visitCount += otherItem->m_visitCount;
512 }
513 
addRedirectURL(const String & url)514 void HistoryItem::addRedirectURL(const String& url)
515 {
516     if (!m_redirectURLs)
517         m_redirectURLs.set(new Vector<String>);
518 
519     // Our API allows us to store all the URLs in the redirect chain, but for
520     // now we only have a use for the final URL.
521     (*m_redirectURLs).resize(1);
522     (*m_redirectURLs)[0] = url;
523 }
524 
redirectURLs() const525 Vector<String>* HistoryItem::redirectURLs() const
526 {
527     return m_redirectURLs.get();
528 }
529 
setRedirectURLs(std::auto_ptr<Vector<String>> redirectURLs)530 void HistoryItem::setRedirectURLs(std::auto_ptr<Vector<String> > redirectURLs)
531 {
532     m_redirectURLs.adopt(redirectURLs);
533 }
534 
535 #ifndef NDEBUG
536 
showTree() const537 int HistoryItem::showTree() const
538 {
539     return showTreeWithIndent(0);
540 }
541 
showTreeWithIndent(unsigned indentLevel) const542 int HistoryItem::showTreeWithIndent(unsigned indentLevel) const
543 {
544     Vector<char> prefix;
545     for (unsigned i = 0; i < indentLevel; ++i)
546         prefix.append("  ", 2);
547     prefix.append("\0", 1);
548 
549     fprintf(stderr, "%s+-%s (%p)\n", prefix.data(), m_urlString.utf8().data(), this);
550 
551     int totalSubItems = 0;
552     for (unsigned i = 0; i < m_subItems.size(); ++i)
553         totalSubItems += m_subItems[i]->showTreeWithIndent(indentLevel + 1);
554     return totalSubItems + 1;
555 }
556 
557 #endif
558 
559 } // namespace WebCore
560 
561 #ifndef NDEBUG
562 
showTree(const WebCore::HistoryItem * item)563 int showTree(const WebCore::HistoryItem* item)
564 {
565     return item->showTree();
566 }
567 
568 #endif
569