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