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