1/*
2    Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
3
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Library General Public
6    License as published by the Free Software Foundation; either
7    version 2 of the License, or (at your option) any later version.
8
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Library General Public License for more details.
13
14    You should have received a copy of the GNU Library General Public License
15    along with this library; see the file COPYING.LIB.  If not, write to
16    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17    Boston, MA 02110-1301, USA.
18*/
19
20#include "config.h"
21#include "qwebhistory.h"
22#include "qwebhistory_p.h"
23
24#include "BackForwardListImpl.h"
25#include "Frame.h"
26#include "IconDatabaseBase.h"
27#include "Image.h"
28#include "IntSize.h"
29#include "KURL.h"
30#include "Page.h"
31#include "PageGroup.h"
32#include <QWebPageAdapter.h>
33#include <wtf/text/WTFString.h>
34
35#include <QSharedData>
36#include <QDebug>
37
38static const int HistoryStreamVersion = 2;
39
40/*!
41  \class QWebHistoryItem
42  \since 4.4
43  \brief The QWebHistoryItem class represents one item in the history of a QWebPage
44
45  \inmodule QtWebKit
46
47  Each QWebHistoryItem instance represents an entry in the history stack of a Web page,
48  containing information about the page, its location, and when it was last visited.
49
50  The following table shows the properties of the page held by the history item, and
51  the functions used to access them.
52
53  \table
54  \header \li Function      \li Description
55  \row    \li title()       \li The page title.
56  \row    \li url()         \li The location of the page.
57  \row    \li originalUrl() \li The URL used to access the page.
58  \row    \li lastVisited() \li The date and time of the user's last visit to the page.
59  \row    \li icon()        \li The icon associated with the page that was provided by the server.
60  \row    \li userData()    \li The user specific data that was stored with the history item.
61  \endtable
62
63  \note QWebHistoryItem objects are value based, but \e{explicitly shared}. Changing
64  a QWebHistoryItem instance by calling setUserData() will change all copies of that
65  instance.
66
67  \sa QWebHistory, QWebPage::history(), QWebHistoryInterface
68*/
69
70/*!
71  Constructs a history item from \a other. The new item and \a other
72  will share their data, and modifying either this item or \a other will
73  modify both instances.
74*/
75QWebHistoryItem::QWebHistoryItem(const QWebHistoryItem &other)
76    : d(other.d)
77{
78}
79
80/*!
81  Assigns the \a other history item to this. This item and \a other
82  will share their data, and modifying either this item or \a other will
83  modify both instances.
84*/
85QWebHistoryItem &QWebHistoryItem::operator=(const QWebHistoryItem &other)
86{
87    d = other.d;
88    return *this;
89}
90
91/*!
92  Destroys the history item.
93*/
94QWebHistoryItem::~QWebHistoryItem()
95{
96}
97
98/*!
99 Returns the original URL associated with the history item.
100
101 \sa url()
102*/
103QUrl QWebHistoryItem::originalUrl() const
104{
105    if (d->item)
106        return d->item->originalURL();
107    return QUrl();
108}
109
110
111/*!
112 Returns the URL associated with the history item.
113
114 \sa originalUrl(), title(), lastVisited()
115*/
116QUrl QWebHistoryItem::url() const
117{
118    if (d->item)
119        return d->item->url();
120    return QUrl();
121}
122
123
124/*!
125 Returns the title of the page associated with the history item.
126
127 \sa icon(), url(), lastVisited()
128*/
129QString QWebHistoryItem::title() const
130{
131    if (d->item)
132        return d->item->title();
133    return QString();
134}
135
136
137/*!
138 Returns the date and time that the page associated with the item was last visited.
139
140 \sa title(), icon(), url()
141*/
142QDateTime QWebHistoryItem::lastVisited() const
143{
144    //FIXME : this will be wrong unless we correctly set lastVisitedTime ourselves
145    if (d->item)
146        return QDateTime::fromTime_t((uint)d->item->lastVisitedTime());
147    return QDateTime();
148}
149
150
151/*!
152 Returns the icon associated with the history item.
153
154 \sa title(), url(), lastVisited()
155*/
156QIcon QWebHistoryItem::icon() const
157{
158    if (d->item)
159        return *WebCore::iconDatabase().synchronousNativeIconForPageURL(d->item->url(), WebCore::IntSize(16, 16));
160
161    return QIcon();
162}
163
164/*!
165  \since 4.5
166  Returns the user specific data that was stored with the history item.
167
168  \sa setUserData()
169*/
170QVariant QWebHistoryItem::userData() const
171{
172    if (d->item)
173        return d->item->userData();
174    return QVariant();
175}
176
177/*!
178  \since 4.5
179
180 Stores user specific data \a userData with the history item.
181
182 \note All copies of this item will be modified.
183
184 \sa userData()
185*/
186void QWebHistoryItem::setUserData(const QVariant& userData)
187{
188    if (d->item)
189        d->item->setUserData(userData);
190}
191
192/*!*
193  \internal
194*/
195QWebHistoryItem::QWebHistoryItem(QWebHistoryItemPrivate *priv)
196{
197    d = priv;
198}
199
200/*!
201    \since 4.5
202    Returns whether this is a valid history item.
203*/
204bool QWebHistoryItem::isValid() const
205{
206    return d->item;
207}
208
209/*!
210  \class QWebHistory
211  \since 4.4
212  \brief The QWebHistory class represents the history of a QWebPage
213
214  \inmodule QtWebKit
215
216  Each QWebPage instance contains a history of visited pages that can be accessed
217  by QWebPage::history(). QWebHistory represents this history and makes it possible
218  to navigate it.
219
220  The history uses the concept of a \e{current item}, dividing the pages visited
221  into those that can be visited by navigating \e back and \e forward using the
222  back() and forward() functions. The current item can be obtained by calling
223  currentItem(), and an arbitrary item in the history can be made the current
224  item by passing it to goToItem().
225
226  A list of items describing the pages that can be visited by going back can be
227  obtained by calling the backItems() function; similarly, items describing the
228  pages ahead of the current page can be obtained with the forwardItems() function.
229  The total list of items is obtained with the items() function.
230
231  Just as with containers, functions are available to examine the history in terms
232  of a list. Arbitrary items in the history can be obtained with itemAt(), the total
233  number of items is given by count(), and the history can be cleared with the
234  clear() function.
235
236  QWebHistory's state can be saved to a QDataStream using the >> operator and loaded
237  by using the << operator.
238
239  \sa QWebHistoryItem, QWebHistoryInterface, QWebPage
240*/
241
242
243QWebHistory::QWebHistory()
244    : d(0)
245{
246}
247
248QWebHistory::~QWebHistory()
249{
250    delete d;
251}
252
253/*!
254  Clears the history.
255
256  \sa count(), items()
257*/
258void QWebHistory::clear()
259{
260    //shortcut to private BackForwardListImpl
261    WebCore::BackForwardListImpl* lst = d->lst;
262
263    //clear visited links
264    WebCore::Page* page = static_cast<WebCore::BackForwardListImpl*>(lst)->page();
265    if (page && page->groupPtr())
266        page->groupPtr()->removeVisitedLinks();
267
268    //if count() == 0 then just return
269    if (!lst->entries().size())
270        return;
271
272    RefPtr<WebCore::HistoryItem> current = lst->currentItem();
273    int capacity = lst->capacity();
274    lst->setCapacity(0);
275
276    lst->setCapacity(capacity);   //revert capacity
277    lst->addItem(current.get());  //insert old current item
278    lst->goToItem(current.get()); //and set it as current again
279
280    d->page()->updateNavigationActions();
281}
282
283/*!
284  Returns a list of all items currently in the history.
285
286  \sa count(), clear()
287*/
288QList<QWebHistoryItem> QWebHistory::items() const
289{
290    const WebCore::HistoryItemVector &items = d->lst->entries();
291
292    QList<QWebHistoryItem> ret;
293    for (unsigned i = 0; i < items.size(); ++i) {
294        QWebHistoryItemPrivate *priv = new QWebHistoryItemPrivate(items[i].get());
295        ret.append(QWebHistoryItem(priv));
296    }
297    return ret;
298}
299
300/*!
301  Returns the list of items in the backwards history list.
302  At most \a maxItems entries are returned.
303
304  \sa forwardItems()
305*/
306QList<QWebHistoryItem> QWebHistory::backItems(int maxItems) const
307{
308    WebCore::HistoryItemVector items(maxItems);
309    d->lst->backListWithLimit(maxItems, items);
310
311    QList<QWebHistoryItem> ret;
312    for (unsigned i = 0; i < items.size(); ++i) {
313        QWebHistoryItemPrivate *priv = new QWebHistoryItemPrivate(items[i].get());
314        ret.append(QWebHistoryItem(priv));
315    }
316    return ret;
317}
318
319/*!
320  Returns the list of items in the forward history list.
321  At most \a maxItems entries are returned.
322
323  \sa backItems()
324*/
325QList<QWebHistoryItem> QWebHistory::forwardItems(int maxItems) const
326{
327    WebCore::HistoryItemVector items(maxItems);
328    d->lst->forwardListWithLimit(maxItems, items);
329
330    QList<QWebHistoryItem> ret;
331    for (unsigned i = 0; i < items.size(); ++i) {
332        QWebHistoryItemPrivate *priv = new QWebHistoryItemPrivate(items[i].get());
333        ret.append(QWebHistoryItem(priv));
334    }
335    return ret;
336}
337
338/*!
339  Returns true if there is an item preceding the current item in the history;
340  otherwise returns false.
341
342  \sa canGoForward()
343*/
344bool QWebHistory::canGoBack() const
345{
346    return d->lst->backListCount() > 0;
347}
348
349/*!
350  Returns true if we have an item to go forward to; otherwise returns false.
351
352  \sa canGoBack()
353*/
354bool QWebHistory::canGoForward() const
355{
356    return d->lst->forwardListCount() > 0;
357}
358
359/*!
360  Set the current item to be the previous item in the history and goes to the
361  corresponding page; i.e., goes back one history item.
362
363  \sa forward(), goToItem()
364*/
365void QWebHistory::back()
366{
367    if (canGoBack()) {
368        WebCore::Page* page = static_cast<WebCore::BackForwardListImpl*>(d->lst)->page();
369        page->goToItem(d->lst->backItem(), WebCore::FrameLoadTypeIndexedBackForward);
370    }
371}
372
373/*!
374  Sets the current item to be the next item in the history and goes to the
375  corresponding page; i.e., goes forward one history item.
376
377  \sa back(), goToItem()
378*/
379void QWebHistory::forward()
380{
381    if (canGoForward()) {
382        WebCore::Page* page = static_cast<WebCore::BackForwardListImpl*>(d->lst)->page();
383        page->goToItem(d->lst->forwardItem(), WebCore::FrameLoadTypeIndexedBackForward);
384    }
385}
386
387/*!
388  Sets the current item to be the specified \a item in the history and goes to the page.
389
390  \sa back(), forward()
391*/
392void QWebHistory::goToItem(const QWebHistoryItem &item)
393{
394    WebCore::Page* page = static_cast<WebCore::BackForwardListImpl*>(d->lst)->page();
395    page->goToItem(item.d->item, WebCore::FrameLoadTypeIndexedBackForward);
396}
397
398/*!
399  Returns the item before the current item in the history.
400*/
401QWebHistoryItem QWebHistory::backItem() const
402{
403    WebCore::HistoryItem *i = d->lst->backItem();
404    QWebHistoryItemPrivate *priv = new QWebHistoryItemPrivate(i);
405    return QWebHistoryItem(priv);
406}
407
408/*!
409  Returns the current item in the history.
410*/
411QWebHistoryItem QWebHistory::currentItem() const
412{
413    WebCore::HistoryItem *i = d->lst->currentItem();
414    QWebHistoryItemPrivate *priv = new QWebHistoryItemPrivate(i);
415    return QWebHistoryItem(priv);
416}
417
418/*!
419  Returns the item after the current item in the history.
420*/
421QWebHistoryItem QWebHistory::forwardItem() const
422{
423    WebCore::HistoryItem *i = d->lst->forwardItem();
424    QWebHistoryItemPrivate *priv = new QWebHistoryItemPrivate(i);
425    return QWebHistoryItem(priv);
426}
427
428/*!
429  \since 4.5
430  Returns the index of the current item in history.
431*/
432int QWebHistory::currentItemIndex() const
433{
434    return d->lst->backListCount();
435}
436
437/*!
438  Returns the item at index \a i in the history.
439*/
440QWebHistoryItem QWebHistory::itemAt(int i) const
441{
442    QWebHistoryItemPrivate *priv;
443    if (i < 0 || i >= count())
444        priv = new QWebHistoryItemPrivate(0);
445    else {
446        WebCore::HistoryItem *item = d->lst->entries()[i].get();
447        priv = new QWebHistoryItemPrivate(item);
448    }
449    return QWebHistoryItem(priv);
450}
451
452/*!
453    Returns the total number of items in the history.
454*/
455int QWebHistory::count() const
456{
457    return d->lst->entries().size();
458}
459
460/*!
461  \since 4.5
462  Returns the maximum number of items in the history.
463
464  \sa setMaximumItemCount()
465*/
466int QWebHistory::maximumItemCount() const
467{
468    return d->lst->capacity();
469}
470
471/*!
472  \since 4.5
473  Sets the maximum number of items in the history to \a count.
474
475  \sa maximumItemCount()
476*/
477void QWebHistory::setMaximumItemCount(int count)
478{
479    d->lst->setCapacity(count);
480}
481
482/*!
483  \since 4.6
484  \fn QDataStream& operator<<(QDataStream& stream, const QWebHistory& history)
485  \relates QWebHistory
486
487  \brief The operator<< function streams a history into a data stream.
488
489  It saves the \a history into the specified \a stream.
490*/
491
492QDataStream& operator<<(QDataStream& target, const QWebHistory& history)
493{
494    QWebHistoryPrivate* d = history.d;
495
496    int version = HistoryStreamVersion;
497
498    target << version;
499    target << history.count() << history.currentItemIndex();
500
501    const WebCore::HistoryItemVector &items = d->lst->entries();
502    for (unsigned i = 0; i < items.size(); i++)
503        items[i].get()->saveState(target, version);
504
505    return target;
506}
507
508/*!
509  \fn QDataStream& operator>>(QDataStream& stream, QWebHistory& history)
510  \relates QWebHistory
511  \since 4.6
512
513  \brief The operator>> function loads a history from a data stream.
514
515  Loads a QWebHistory from the specified \a stream into the given \a history.
516*/
517
518QDataStream& operator>>(QDataStream& source, QWebHistory& history)
519{
520    QWebHistoryPrivate* d = history.d;
521    // Clear first, to have the same behavior if our version doesn't match and if the HistoryItem's version doesn't.
522    history.clear();
523
524    // This version covers every field we serialize in qwebhistory.cpp and HistoryItemQt.cpp (like the HistoryItem::userData()).
525    // HistoryItem has its own version in the stream covering the work done in encodeBackForwardTree.
526    // If any of those two stream version changes, the effect should be the same and the QWebHistory should fail to restore.
527    int version;
528    source >> version;
529    if (version != HistoryStreamVersion) {
530        // We do not try to decode previous history stream versions.
531        // Make sure that our history is cleared and mark the rest of the stream as invalid.
532        ASSERT(history.count() == 1);
533        source.setStatus(QDataStream::ReadCorruptData);
534        return source;
535    }
536
537    int count;
538    int currentIndex;
539    source >> count >> currentIndex;
540
541    // only if there are elements
542    if (count) {
543        // after clear() is new clear HistoryItem (at the end we had to remove it)
544        WebCore::HistoryItem* nullItem = d->lst->currentItem();
545        for (int i = 0; i < count; i++) {
546            WTF::RefPtr<WebCore::HistoryItem> item = WebCore::HistoryItem::restoreState(source, version);
547            if (!item) {
548                // The HistoryItem internal version might have changed, do the same as when our own version change.
549                history.clear();
550                source.setStatus(QDataStream::ReadCorruptData);
551                return source;
552            }
553            d->lst->addItem(item);
554        }
555        d->lst->removeItem(nullItem);
556        history.goToItem(history.itemAt(currentIndex));
557    }
558
559    d->page()->updateNavigationActions();
560
561    return source;
562}
563
564QWebPageAdapter* QWebHistoryPrivate::page()
565{
566    return QWebPageAdapter::kit(static_cast<WebCore::BackForwardListImpl*>(lst)->page());
567}
568
569WebCore::HistoryItem* QWebHistoryItemPrivate::core(const QWebHistoryItem* q)
570{
571    return q->d->item;
572}
573