OSDN Git Service

am 48a84519: (-s ours) am 31cf3287: Do not merge: Cherry-pick CL to help flash plugin...
[android-x86/external-webkit.git] / WebCore / history / HistoryItem.cpp
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 "CachedPage.h"
30 #include "Document.h"
31 #include "IconDatabase.h"
32 #include "PageCache.h"
33 #include "ResourceRequest.h"
34 #include <stdio.h>
35 #include <wtf/CurrentTime.h>
36 #include <wtf/MathExtras.h>
37 #include <wtf/text/CString.h>
38
39 namespace WebCore {
40
41 static long long generateSequenceNumber()
42 {
43     // Initialize to the current time to reduce the likelihood of generating
44     // identifiers that overlap with those from past/future browser sessions.
45     static long long next = static_cast<long long>(currentTime() * 1000000.0);
46     return ++next;
47 }
48
49 static void defaultNotifyHistoryItemChanged(HistoryItem*)
50 {
51 }
52
53 void (*notifyHistoryItemChanged)(HistoryItem*) = defaultNotifyHistoryItemChanged;
54
55 HistoryItem::HistoryItem()
56     : m_lastVisitedTime(0)
57     , m_lastVisitWasHTTPNonGet(false)
58     , m_lastVisitWasFailure(false)
59     , m_isTargetItem(false)
60     , m_visitCount(0)
61     , m_itemSequenceNumber(generateSequenceNumber())
62     , m_documentSequenceNumber(generateSequenceNumber())
63     , m_next(0)
64     , m_prev(0)
65 {
66 }
67
68 HistoryItem::HistoryItem(const String& urlString, const String& title, double time)
69     : m_urlString(urlString)
70     , m_originalURLString(urlString)
71     , m_title(title)
72     , m_lastVisitedTime(time)
73     , m_lastVisitWasHTTPNonGet(false)
74     , m_lastVisitWasFailure(false)
75     , m_isTargetItem(false)
76     , m_visitCount(0)
77     , m_itemSequenceNumber(generateSequenceNumber())
78     , m_documentSequenceNumber(generateSequenceNumber())
79     , m_next(0)
80     , m_prev(0)
81 {    
82     iconDatabase()->retainIconForPageURL(m_urlString);
83 }
84
85 HistoryItem::HistoryItem(const String& urlString, const String& title, const String& alternateTitle, double time)
86     : m_urlString(urlString)
87     , m_originalURLString(urlString)
88     , m_title(title)
89     , m_displayTitle(alternateTitle)
90     , m_lastVisitedTime(time)
91     , m_lastVisitWasHTTPNonGet(false)
92     , m_lastVisitWasFailure(false)
93     , m_isTargetItem(false)
94     , m_visitCount(0)
95     , m_itemSequenceNumber(generateSequenceNumber())
96     , m_documentSequenceNumber(generateSequenceNumber())
97     , m_next(0)
98     , m_prev(0)
99 {
100     iconDatabase()->retainIconForPageURL(m_urlString);
101 }
102
103 HistoryItem::HistoryItem(const KURL& url, const String& target, const String& parent, const String& title)
104     : m_urlString(url.string())
105     , m_originalURLString(url.string())
106     , m_target(target)
107     , m_parent(parent)
108     , m_title(title)
109     , m_lastVisitedTime(0)
110     , m_lastVisitWasHTTPNonGet(false)
111     , m_lastVisitWasFailure(false)
112     , m_isTargetItem(false)
113     , m_visitCount(0)
114     , m_itemSequenceNumber(generateSequenceNumber())
115     , m_documentSequenceNumber(generateSequenceNumber())
116     , m_next(0)
117     , m_prev(0)
118 {    
119     iconDatabase()->retainIconForPageURL(m_urlString);
120 }
121
122 HistoryItem::~HistoryItem()
123 {
124     ASSERT(!m_cachedPage);
125     iconDatabase()->releaseIconForPageURL(m_urlString);
126 #if PLATFORM(ANDROID)
127     if (m_bridge)
128         m_bridge->detachHistoryItem();
129 #endif
130 }
131
132 inline HistoryItem::HistoryItem(const HistoryItem& item)
133     : RefCounted<HistoryItem>()
134     , m_urlString(item.m_urlString)
135     , m_originalURLString(item.m_originalURLString)
136     , m_referrer(item.m_referrer)
137     , m_target(item.m_target)
138     , m_parent(item.m_parent)
139     , m_title(item.m_title)
140     , m_displayTitle(item.m_displayTitle)
141     , m_lastVisitedTime(item.m_lastVisitedTime)
142     , m_lastVisitWasHTTPNonGet(item.m_lastVisitWasHTTPNonGet)
143     , m_scrollPoint(item.m_scrollPoint)
144     , m_lastVisitWasFailure(item.m_lastVisitWasFailure)
145     , m_isTargetItem(item.m_isTargetItem)
146     , m_visitCount(item.m_visitCount)
147     , m_dailyVisitCounts(item.m_dailyVisitCounts)
148     , m_weeklyVisitCounts(item.m_weeklyVisitCounts)
149     , m_itemSequenceNumber(item.m_itemSequenceNumber)
150     , m_documentSequenceNumber(item.m_documentSequenceNumber)
151     , m_formContentType(item.m_formContentType)
152 {
153     if (item.m_formData)
154         m_formData = item.m_formData->copy();
155         
156     unsigned size = item.m_children.size();
157     m_children.reserveInitialCapacity(size);
158     for (unsigned i = 0; i < size; ++i)
159         m_children.uncheckedAppend(item.m_children[i]->copy());
160
161     if (item.m_redirectURLs)
162         m_redirectURLs = adoptPtr(new Vector<String>(*item.m_redirectURLs));
163 }
164
165 PassRefPtr<HistoryItem> HistoryItem::copy() const
166 {
167     return adoptRef(new HistoryItem(*this));
168 }
169
170 void HistoryItem::reset()
171 {
172     iconDatabase()->releaseIconForPageURL(m_urlString);
173
174     m_urlString = String();
175     m_originalURLString = String();
176     m_referrer = String();
177     m_target = String();
178     m_parent = String();
179     m_title = String();
180     m_displayTitle = String();
181
182     m_lastVisitedTime = 0;
183     m_lastVisitWasHTTPNonGet = false;
184
185     m_lastVisitWasFailure = false;
186     m_isTargetItem = false;
187     m_visitCount = 0;
188     m_dailyVisitCounts.clear();
189     m_weeklyVisitCounts.clear();
190
191     m_redirectURLs.clear();
192
193     m_itemSequenceNumber = generateSequenceNumber();
194
195     m_stateObject = 0;
196     m_documentSequenceNumber = generateSequenceNumber();
197
198     m_formData = 0;
199     m_formContentType = String();
200 }
201
202 const String& HistoryItem::urlString() const
203 {
204     return m_urlString;
205 }
206
207 // The first URL we loaded to get to where this history item points.  Includes both client
208 // and server redirects.
209 const String& HistoryItem::originalURLString() const
210 {
211     return m_originalURLString;
212 }
213
214 const String& HistoryItem::title() const
215 {
216     return m_title;
217 }
218
219 const String& HistoryItem::alternateTitle() const
220 {
221     return m_displayTitle;
222 }
223
224 Image* HistoryItem::icon() const
225 {
226     Image* result = iconDatabase()->iconForPageURL(m_urlString, IntSize(16, 16));
227     return result ? result : iconDatabase()->defaultIcon(IntSize(16, 16));
228 }
229
230 double HistoryItem::lastVisitedTime() const
231 {
232     return m_lastVisitedTime;
233 }
234
235 KURL HistoryItem::url() const
236 {
237     return KURL(ParsedURLString, m_urlString);
238 }
239
240 KURL HistoryItem::originalURL() const
241 {
242     return KURL(ParsedURLString, m_originalURLString);
243 }
244
245 const String& HistoryItem::referrer() const
246 {
247     return m_referrer;
248 }
249
250 const String& HistoryItem::target() const
251 {
252     return m_target;
253 }
254
255 const String& HistoryItem::parent() const
256 {
257     return m_parent;
258 }
259
260 void HistoryItem::setAlternateTitle(const String& alternateTitle)
261 {
262     m_displayTitle = alternateTitle;
263     notifyHistoryItemChanged(this);
264 }
265
266 void HistoryItem::setURLString(const String& urlString)
267 {
268     if (m_urlString != urlString) {
269         iconDatabase()->releaseIconForPageURL(m_urlString);
270         m_urlString = urlString;
271         iconDatabase()->retainIconForPageURL(m_urlString);
272     }
273     
274     notifyHistoryItemChanged(this);
275 }
276
277 void HistoryItem::setURL(const KURL& url)
278 {
279     pageCache()->remove(this);
280     setURLString(url.string());
281     clearDocumentState();
282 }
283
284 void HistoryItem::setOriginalURLString(const String& urlString)
285 {
286     m_originalURLString = urlString;
287     notifyHistoryItemChanged(this);
288 }
289
290 void HistoryItem::setReferrer(const String& referrer)
291 {
292     m_referrer = referrer;
293     notifyHistoryItemChanged(this);
294 }
295
296 void HistoryItem::setTitle(const String& title)
297 {
298     m_title = title;
299     notifyHistoryItemChanged(this);
300 }
301
302 void HistoryItem::setTarget(const String& target)
303 {
304     m_target = target;
305     notifyHistoryItemChanged(this);
306 }
307
308 void HistoryItem::setParent(const String& parent)
309 {
310     m_parent = parent;
311 }
312
313 static inline int timeToDay(double time)
314 {
315     static const double secondsPerDay = 60 * 60 * 24;
316     return static_cast<int>(ceil(time / secondsPerDay));
317 }
318
319 void HistoryItem::padDailyCountsForNewVisit(double time)
320 {
321     if (m_dailyVisitCounts.isEmpty())
322         m_dailyVisitCounts.prepend(m_visitCount);
323
324     int daysElapsed = timeToDay(time) - timeToDay(m_lastVisitedTime);
325
326     if (daysElapsed < 0)
327       daysElapsed = 0;
328
329     Vector<int> padding;
330     padding.fill(0, daysElapsed);
331     m_dailyVisitCounts.prepend(padding);
332 }
333
334 static const size_t daysPerWeek = 7;
335 static const size_t maxDailyCounts = 2 * daysPerWeek - 1;
336 static const size_t maxWeeklyCounts = 5;
337
338 void HistoryItem::collapseDailyVisitsToWeekly()
339 {
340     while (m_dailyVisitCounts.size() > maxDailyCounts) {
341         int oldestWeekTotal = 0;
342         for (size_t i = 0; i < daysPerWeek; i++)
343             oldestWeekTotal += m_dailyVisitCounts[m_dailyVisitCounts.size() - daysPerWeek + i];
344         m_dailyVisitCounts.shrink(m_dailyVisitCounts.size() - daysPerWeek);
345         m_weeklyVisitCounts.prepend(oldestWeekTotal);
346     }
347
348     if (m_weeklyVisitCounts.size() > maxWeeklyCounts)
349         m_weeklyVisitCounts.shrink(maxWeeklyCounts);
350 }
351
352 void HistoryItem::recordVisitAtTime(double time, VisitCountBehavior visitCountBehavior)
353 {
354     padDailyCountsForNewVisit(time);
355
356     m_lastVisitedTime = time;
357
358     if (visitCountBehavior == IncreaseVisitCount) {
359         ++m_visitCount;
360         ++m_dailyVisitCounts[0];
361     }
362
363     collapseDailyVisitsToWeekly();
364 }
365
366 void HistoryItem::setLastVisitedTime(double time)
367 {
368     if (m_lastVisitedTime != time)
369         recordVisitAtTime(time);
370 }
371
372 void HistoryItem::visited(const String& title, double time, VisitCountBehavior visitCountBehavior)
373 {
374     m_title = title;
375     recordVisitAtTime(time, visitCountBehavior);
376 }
377
378 int HistoryItem::visitCount() const
379 {
380     return m_visitCount;
381 }
382
383 void HistoryItem::recordInitialVisit()
384 {
385     ASSERT(!m_visitCount);
386     recordVisitAtTime(m_lastVisitedTime);
387 }
388
389 void HistoryItem::setVisitCount(int count)
390 {
391     m_visitCount = count;
392 }
393
394 void HistoryItem::adoptVisitCounts(Vector<int>& dailyCounts, Vector<int>& weeklyCounts)
395 {
396     m_dailyVisitCounts.clear();
397     m_dailyVisitCounts.swap(dailyCounts);
398     m_weeklyVisitCounts.clear();
399     m_weeklyVisitCounts.swap(weeklyCounts);
400 }
401
402 const IntPoint& HistoryItem::scrollPoint() const
403 {
404     return m_scrollPoint;
405 }
406
407 void HistoryItem::setScrollPoint(const IntPoint& point)
408 {
409     m_scrollPoint = point;
410 }
411
412 void HistoryItem::clearScrollPoint()
413 {
414     m_scrollPoint.setX(0);
415     m_scrollPoint.setY(0);
416 }
417
418 void HistoryItem::setDocumentState(const Vector<String>& state)
419 {
420     m_documentState = state;
421 #if PLATFORM(ANDROID)
422     notifyHistoryItemChanged(this);
423 #endif
424 }
425
426 const Vector<String>& HistoryItem::documentState() const
427 {
428     return m_documentState;
429 }
430
431 void HistoryItem::clearDocumentState()
432 {
433     m_documentState.clear();
434 #if PLATFORM(ANDROID)
435     notifyHistoryItemChanged(this);
436 #endif
437 }
438
439 bool HistoryItem::isTargetItem() const
440 {
441     return m_isTargetItem;
442 }
443
444 void HistoryItem::setIsTargetItem(bool flag)
445 {
446     m_isTargetItem = flag;
447 #if PLATFORM(ANDROID)
448     notifyHistoryItemChanged(this);
449 #endif
450 }
451
452 void HistoryItem::setStateObject(PassRefPtr<SerializedScriptValue> object)
453 {
454     m_stateObject = object;
455 }
456
457 void HistoryItem::addChildItem(PassRefPtr<HistoryItem> child)
458 {
459     ASSERT(!childItemWithTarget(child->target()));
460     m_children.append(child);
461 #if PLATFORM(ANDROID)
462     notifyHistoryItemChanged(this);
463 #endif
464 }
465
466 void HistoryItem::setChildItem(PassRefPtr<HistoryItem> child)
467 {
468     ASSERT(!child->isTargetItem());
469     unsigned size = m_children.size();
470     for (unsigned i = 0; i < size; ++i)  {
471         if (m_children[i]->target() == child->target()) {
472             child->setIsTargetItem(m_children[i]->isTargetItem());
473             m_children[i] = child;
474             return;
475         }
476     }
477     m_children.append(child);
478 }
479
480 HistoryItem* HistoryItem::childItemWithTarget(const String& target) const
481 {
482     unsigned size = m_children.size();
483     for (unsigned i = 0; i < size; ++i) {
484         if (m_children[i]->target() == target)
485             return m_children[i].get();
486     }
487     return 0;
488 }
489
490 HistoryItem* HistoryItem::childItemWithDocumentSequenceNumber(long long number) const
491 {
492     unsigned size = m_children.size();
493     for (unsigned i = 0; i < size; ++i) {
494         if (m_children[i]->documentSequenceNumber() == number)
495             return m_children[i].get();
496     }
497     return 0;
498 }
499
500 // <rdar://problem/4895849> HistoryItem::findTargetItem() should be replaced with a non-recursive method.
501 HistoryItem* HistoryItem::findTargetItem()
502 {
503     if (m_isTargetItem)
504         return this;
505     unsigned size = m_children.size();
506     for (unsigned i = 0; i < size; ++i) {
507         if (HistoryItem* match = m_children[i]->targetItem())
508             return match;
509     }
510     return 0;
511 }
512
513 HistoryItem* HistoryItem::targetItem()
514 {
515     HistoryItem* foundItem = findTargetItem();
516     return foundItem ? foundItem : this;
517 }
518
519 const HistoryItemVector& HistoryItem::children() const
520 {
521     return m_children;
522 }
523
524 bool HistoryItem::hasChildren() const
525 {
526     return !m_children.isEmpty();
527 }
528
529 void HistoryItem::clearChildren()
530 {
531     m_children.clear();
532 }
533
534 // We do same-document navigation if going to a different item and if either of the following is true:
535 // - The other item corresponds to the same document (for history entries created via pushState or fragment changes).
536 // - The other item corresponds to the same set of documents, including frames (for history entries created via regular navigation)
537 bool HistoryItem::shouldDoSameDocumentNavigationTo(HistoryItem* otherItem) const
538 {
539     if (this == otherItem)
540         return false;
541
542     if (stateObject() || otherItem->stateObject())
543         return documentSequenceNumber() == otherItem->documentSequenceNumber();
544     
545     if ((url().hasFragmentIdentifier() || otherItem->url().hasFragmentIdentifier()) && equalIgnoringFragmentIdentifier(url(), otherItem->url()))
546         return documentSequenceNumber() == otherItem->documentSequenceNumber();        
547     
548     return hasSameDocumentTree(otherItem);
549 }
550
551 // Does a recursive check that this item and its descendants have the same
552 // document sequence numbers as the other item.
553 bool HistoryItem::hasSameDocumentTree(HistoryItem* otherItem) const
554 {
555     if (documentSequenceNumber() != otherItem->documentSequenceNumber())
556         return false;
557         
558     if (children().size() != otherItem->children().size())
559         return false;
560
561     for (size_t i = 0; i < children().size(); i++) {
562         HistoryItem* child = children()[i].get();
563         HistoryItem* otherChild = otherItem->childItemWithDocumentSequenceNumber(child->documentSequenceNumber());
564         if (!otherChild || !child->hasSameDocumentTree(otherChild))
565             return false;
566     }
567
568     return true;
569 }
570
571 // Does a non-recursive check that this item and its immediate children have the
572 // same frames as the other item.
573 bool HistoryItem::hasSameFrames(HistoryItem* otherItem) const
574 {
575     if (target() != otherItem->target())
576         return false;
577         
578     if (children().size() != otherItem->children().size())
579         return false;
580
581     for (size_t i = 0; i < children().size(); i++) {
582         if (!otherItem->childItemWithTarget(children()[i]->target()))
583             return false;
584     }
585
586     return true;
587 }
588
589 String HistoryItem::formContentType() const
590 {
591     return m_formContentType;
592 }
593
594 void HistoryItem::setFormInfoFromRequest(const ResourceRequest& request)
595 {
596     m_referrer = request.httpReferrer();
597     
598     if (equalIgnoringCase(request.httpMethod(), "POST")) {
599         // FIXME: Eventually we have to make this smart enough to handle the case where
600         // we have a stream for the body to handle the "data interspersed with files" feature.
601         m_formData = request.httpBody();
602         m_formContentType = request.httpContentType();
603     } else {
604         m_formData = 0;
605         m_formContentType = String();
606     }
607 #if PLATFORM(ANDROID)
608     notifyHistoryItemChanged(this);
609 #endif
610 }
611
612 void HistoryItem::setFormData(PassRefPtr<FormData> formData)
613 {
614     m_formData = formData;
615 }
616
617 void HistoryItem::setFormContentType(const String& formContentType)
618 {
619     m_formContentType = formContentType;
620 }
621
622 FormData* HistoryItem::formData()
623 {
624     return m_formData.get();
625 }
626
627 bool HistoryItem::isCurrentDocument(Document* doc) const
628 {
629     // FIXME: We should find a better way to check if this is the current document.
630     return equalIgnoringFragmentIdentifier(url(), doc->url());
631 }
632
633 void HistoryItem::mergeAutoCompleteHints(HistoryItem* otherItem)
634 {
635     // FIXME: this is broken - we should be merging the daily counts
636     // somehow.  but this is to support API that's not really used in
637     // practice so leave it broken for now.
638     ASSERT(otherItem);
639     if (otherItem != this)
640         m_visitCount += otherItem->m_visitCount;
641 }
642
643 void HistoryItem::addRedirectURL(const String& url)
644 {
645     if (!m_redirectURLs)
646         m_redirectURLs = adoptPtr(new Vector<String>);
647
648     // Our API allows us to store all the URLs in the redirect chain, but for
649     // now we only have a use for the final URL.
650     (*m_redirectURLs).resize(1);
651     (*m_redirectURLs)[0] = url;
652 }
653
654 Vector<String>* HistoryItem::redirectURLs() const
655 {
656     return m_redirectURLs.get();
657 }
658
659 void HistoryItem::setRedirectURLs(PassOwnPtr<Vector<String> > redirectURLs)
660 {
661     m_redirectURLs = redirectURLs;
662 }
663
664 #ifndef NDEBUG
665
666 int HistoryItem::showTree() const
667 {
668     return showTreeWithIndent(0);
669 }
670
671 int HistoryItem::showTreeWithIndent(unsigned indentLevel) const
672 {
673     Vector<char> prefix;
674     for (unsigned i = 0; i < indentLevel; ++i)
675         prefix.append("  ", 2);
676     prefix.append("\0", 1);
677
678     fprintf(stderr, "%s+-%s (%p)\n", prefix.data(), m_urlString.utf8().data(), this);
679     
680     int totalSubItems = 0;
681     for (unsigned i = 0; i < m_children.size(); ++i)
682         totalSubItems += m_children[i]->showTreeWithIndent(indentLevel + 1);
683     return totalSubItems + 1;
684 }
685
686 #endif
687                 
688 } // namespace WebCore
689
690 #ifndef NDEBUG
691
692 int showTree(const WebCore::HistoryItem* item)
693 {
694     return item->showTree();
695 }
696
697 #endif