1/*
2 * Copyright (C) 2008, 2009 Jan Michael C. Alonzo
3 * Copyright (C) 2009 Igalia S.L.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB.  If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21#include "config.h"
22#include "webkitwebhistoryitem.h"
23
24#include "HistoryItem.h"
25#include "KURL.h"
26#include "webkitglobalsprivate.h"
27#include "webkitwebhistoryitemprivate.h"
28#include <glib.h>
29#include <glib/gi18n-lib.h>
30#include <wtf/text/CString.h>
31#include <wtf/text/WTFString.h>
32
33/**
34 * SECTION:webkitwebhistoryitem
35 * @short_description: One item of the #WebKitWebBackForwardList and or global history
36 * @see_also: #WebKitWebBackForwardList
37 *
38 * A history item consists out of a title and a uri. It can be part of the
39 * #WebKitWebBackForwardList and the global history. The global history is used
40 * for coloring the links of visited sites.  #WebKitWebHistoryItem's constructed with
41 * #webkit_web_history_item_new and #webkit_web_history_item_new_with_data are
42 * automatically added to the global history.
43 *
44 * <informalexample><programlisting>
45 * /<!-- -->* Inject a visited page into the global history *<!-- -->/
46 * webkit_web_history_item_new_with_data("http://www.gnome.org/", "GNOME: The Free Software Desktop Project");
47 * webkit_web_history_item_new_with_data("http://www.webkit.org/", "The WebKit Open Source Project");
48 * </programlisting></informalexample>
49 *
50 */
51
52using namespace WebKit;
53
54struct _WebKitWebHistoryItemPrivate {
55    WebCore::HistoryItem* historyItem;
56
57    WTF::CString title;
58    WTF::CString alternateTitle;
59    WTF::CString uri;
60    WTF::CString originalUri;
61
62    gboolean disposed;
63};
64
65enum {
66    PROP_0,
67
68    PROP_TITLE,
69    PROP_ALTERNATE_TITLE,
70    PROP_URI,
71    PROP_ORIGINAL_URI,
72    PROP_LAST_VISITED_TIME
73};
74
75G_DEFINE_TYPE(WebKitWebHistoryItem, webkit_web_history_item, G_TYPE_OBJECT);
76
77static void webkit_web_history_item_set_property(GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec);
78
79static void webkit_web_history_item_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec* pspec);
80
81GHashTable* webkit_history_items()
82{
83    static GHashTable* historyItems = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_object_unref);
84    return historyItems;
85}
86
87void webkit_history_item_add(WebKitWebHistoryItem* webHistoryItem, WebCore::HistoryItem* historyItem)
88{
89    g_return_if_fail(WEBKIT_IS_WEB_HISTORY_ITEM(webHistoryItem));
90
91    GHashTable* table = webkit_history_items();
92    g_hash_table_insert(table, historyItem, webHistoryItem);
93}
94
95static void webkit_web_history_item_dispose(GObject* object)
96{
97    WebKitWebHistoryItem* webHistoryItem = WEBKIT_WEB_HISTORY_ITEM(object);
98    WebKitWebHistoryItemPrivate* priv = webHistoryItem->priv;
99
100    if (!priv->disposed) {
101        WebCore::HistoryItem* item = core(webHistoryItem);
102        item->deref();
103        priv->disposed = true;
104    }
105
106    G_OBJECT_CLASS(webkit_web_history_item_parent_class)->dispose(object);
107}
108
109static void webkit_web_history_item_finalize(GObject* object)
110{
111    WebKitWebHistoryItem* webHistoryItem = WEBKIT_WEB_HISTORY_ITEM(object);
112    WebKitWebHistoryItemPrivate* priv = webHistoryItem->priv;
113
114    priv->title = WTF::CString();
115    priv->alternateTitle = WTF::CString();
116    priv->uri = WTF::CString();
117    priv->originalUri = WTF::CString();
118
119    G_OBJECT_CLASS(webkit_web_history_item_parent_class)->finalize(object);
120}
121
122static void webkit_web_history_item_class_init(WebKitWebHistoryItemClass* klass)
123{
124    GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
125
126    gobject_class->dispose = webkit_web_history_item_dispose;
127    gobject_class->finalize = webkit_web_history_item_finalize;
128    gobject_class->set_property = webkit_web_history_item_set_property;
129    gobject_class->get_property = webkit_web_history_item_get_property;
130
131    webkitInit();
132
133    /**
134    * WebKitWebHistoryItem:title:
135    *
136    * The title of the history item.
137    *
138    * Since: 1.0.2
139    */
140    g_object_class_install_property(gobject_class,
141                                    PROP_TITLE,
142                                    g_param_spec_string(
143                                    "title",
144                                    _("Title"),
145                                    _("The title of the history item"),
146                                    NULL,
147                                    WEBKIT_PARAM_READABLE));
148
149    /**
150    * WebKitWebHistoryItem:alternate-title:
151    *
152    * The alternate title of the history item.
153    *
154    * Since: 1.0.2
155    */
156    g_object_class_install_property(gobject_class,
157                                    PROP_ALTERNATE_TITLE,
158                                    g_param_spec_string(
159                                    "alternate-title",
160                                    _("Alternate Title"),
161                                    _("The alternate title of the history item"),
162                                    NULL,
163                                    WEBKIT_PARAM_READWRITE));
164
165    /**
166    * WebKitWebHistoryItem:uri:
167    *
168    * The URI of the history item.
169    *
170    * Since: 1.0.2
171    */
172    g_object_class_install_property(gobject_class,
173                                    PROP_URI,
174                                    g_param_spec_string(
175                                    "uri",
176                                    _("URI"),
177                                    _("The URI of the history item"),
178                                    NULL,
179                                    WEBKIT_PARAM_READABLE));
180
181    /**
182    * WebKitWebHistoryItem:original-uri:
183    *
184    * The original URI of the history item.
185    *
186    * Since: 1.0.2
187    */
188    g_object_class_install_property(gobject_class,
189                                    PROP_ORIGINAL_URI,
190                                    g_param_spec_string(
191                                    "original-uri",
192                                    _("Original URI"),
193                                    _("The original URI of the history item"),
194                                    NULL,
195                                    WEBKIT_PARAM_READABLE));
196
197   /**
198    * WebKitWebHistoryItem:last-visited-time:
199    *
200    * The time at which the history item was last visited.
201    *
202    * Since: 1.0.2
203    */
204    g_object_class_install_property(gobject_class,
205                                    PROP_LAST_VISITED_TIME,
206                                    g_param_spec_double(
207                                    "last-visited-time",
208                                    _("Last visited Time"),
209                                    _("The time at which the history item was last visited"),
210                                    0, G_MAXDOUBLE, 0,
211                                    WEBKIT_PARAM_READABLE));
212
213    g_type_class_add_private(gobject_class, sizeof(WebKitWebHistoryItemPrivate));
214}
215
216static void webkit_web_history_item_init(WebKitWebHistoryItem* webHistoryItem)
217{
218    webHistoryItem->priv = G_TYPE_INSTANCE_GET_PRIVATE(webHistoryItem, WEBKIT_TYPE_WEB_HISTORY_ITEM, WebKitWebHistoryItemPrivate);
219}
220
221static void webkit_web_history_item_set_property(GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec)
222{
223    WebKitWebHistoryItem* webHistoryItem = WEBKIT_WEB_HISTORY_ITEM(object);
224
225    switch(prop_id) {
226    case PROP_ALTERNATE_TITLE:
227        webkit_web_history_item_set_alternate_title(webHistoryItem, g_value_get_string(value));
228        break;
229    default:
230        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
231        break;
232    }
233}
234
235static void webkit_web_history_item_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec* pspec)
236{
237    WebKitWebHistoryItem* webHistoryItem = WEBKIT_WEB_HISTORY_ITEM(object);
238
239    switch (prop_id) {
240    case PROP_TITLE:
241        g_value_set_string(value, webkit_web_history_item_get_title(webHistoryItem));
242        break;
243    case PROP_ALTERNATE_TITLE:
244        g_value_set_string(value, webkit_web_history_item_get_alternate_title(webHistoryItem));
245        break;
246    case PROP_URI:
247        g_value_set_string(value, webkit_web_history_item_get_uri(webHistoryItem));
248        break;
249    case PROP_ORIGINAL_URI:
250        g_value_set_string(value, webkit_web_history_item_get_original_uri(webHistoryItem));
251        break;
252    case PROP_LAST_VISITED_TIME:
253        g_value_set_double(value, webkit_web_history_item_get_last_visited_time(webHistoryItem));
254        break;
255    default:
256        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
257        break;
258    }
259}
260
261/* Helper function to create a new WebHistoryItem instance when needed */
262WebKitWebHistoryItem* webkit_web_history_item_new_with_core_item(PassRefPtr<WebCore::HistoryItem> historyItem)
263{
264    return kit(historyItem);
265}
266
267
268/**
269 * webkit_web_history_item_new:
270 *
271 * Creates a new #WebKitWebHistoryItem instance
272 *
273 * Return value: the new #WebKitWebHistoryItem
274 */
275WebKitWebHistoryItem* webkit_web_history_item_new()
276{
277    WebKitWebHistoryItem* webHistoryItem = WEBKIT_WEB_HISTORY_ITEM(g_object_new(WEBKIT_TYPE_WEB_HISTORY_ITEM, NULL));
278    WebKitWebHistoryItemPrivate* priv = webHistoryItem->priv;
279
280    RefPtr<WebCore::HistoryItem> item = WebCore::HistoryItem::create();
281    priv->historyItem = item.release().leakRef();
282    webkit_history_item_add(webHistoryItem, priv->historyItem);
283
284    return webHistoryItem;
285}
286
287/**
288 * webkit_web_history_item_new_with_data:
289 * @uri: the URI of the page
290 * @title: the title of the page
291 *
292 * Creates a new #WebKitWebHistoryItem with the given URI and title
293 *
294 * Return value: the new #WebKitWebHistoryItem
295 */
296WebKitWebHistoryItem* webkit_web_history_item_new_with_data(const gchar* uri, const gchar* title)
297{
298    WebKitWebHistoryItem* webHistoryItem = WEBKIT_WEB_HISTORY_ITEM(g_object_new(WEBKIT_TYPE_WEB_HISTORY_ITEM, NULL));
299    WebKitWebHistoryItemPrivate* priv = webHistoryItem->priv;
300
301    WebCore::KURL historyUri(WebCore::KURL(), uri);
302    WTF::String historyTitle = WTF::String::fromUTF8(title);
303    RefPtr<WebCore::HistoryItem> item = WebCore::HistoryItem::create(historyUri, historyTitle, 0);
304    priv->historyItem = item.release().leakRef();
305    webkit_history_item_add(webHistoryItem, priv->historyItem);
306
307    return webHistoryItem;
308}
309
310/**
311 * webkit_web_history_item_get_title:
312 * @web_history_item: a #WebKitWebHistoryItem
313 *
314 * Returns: the page title of @web_history_item
315 */
316const gchar* webkit_web_history_item_get_title(WebKitWebHistoryItem* webHistoryItem)
317{
318    g_return_val_if_fail(WEBKIT_IS_WEB_HISTORY_ITEM(webHistoryItem), NULL);
319
320    WebCore::HistoryItem* item = core(webHistoryItem);
321
322    g_return_val_if_fail(item, NULL);
323
324    WebKitWebHistoryItemPrivate* priv = webHistoryItem->priv;
325    priv->title = item->title().utf8();
326
327    return priv->title.data();
328}
329
330/**
331 * webkit_web_history_item_get_alternate_title:
332 * @web_history_item: a #WebKitWebHistoryItem
333 *
334 * Returns the alternate title of @web_history_item
335 *
336 * Return value: the alternate title of @web_history_item
337 */
338const gchar* webkit_web_history_item_get_alternate_title(WebKitWebHistoryItem* webHistoryItem)
339{
340    g_return_val_if_fail(WEBKIT_IS_WEB_HISTORY_ITEM(webHistoryItem), NULL);
341
342    WebCore::HistoryItem* item = core(webHistoryItem);
343
344    g_return_val_if_fail(item, NULL);
345
346    WebKitWebHistoryItemPrivate* priv = webHistoryItem->priv;
347    priv->alternateTitle = item->alternateTitle().utf8();
348
349    return priv->alternateTitle.data();
350}
351
352/**
353 * webkit_web_history_item_set_alternate_title:
354 * @web_history_item: a #WebKitWebHistoryItem
355 * @title: the alternate title for @this history item
356 *
357 * Sets an alternate title for @web_history_item
358 */
359void webkit_web_history_item_set_alternate_title(WebKitWebHistoryItem* webHistoryItem, const gchar* title)
360{
361    g_return_if_fail(WEBKIT_IS_WEB_HISTORY_ITEM(webHistoryItem));
362    g_return_if_fail(title);
363
364    WebCore::HistoryItem* item = core(webHistoryItem);
365
366    item->setAlternateTitle(WTF::String::fromUTF8(title));
367    g_object_notify(G_OBJECT(webHistoryItem), "alternate-title");
368}
369
370/**
371 * webkit_web_history_item_get_uri:
372 * @web_history_item: a #WebKitWebHistoryItem
373 *
374 * Returns the URI of @this
375 *
376 * Return value: the URI of @web_history_item
377 */
378const gchar* webkit_web_history_item_get_uri(WebKitWebHistoryItem* webHistoryItem)
379{
380    g_return_val_if_fail(WEBKIT_IS_WEB_HISTORY_ITEM(webHistoryItem), NULL);
381
382    WebCore::HistoryItem* item = core(WEBKIT_WEB_HISTORY_ITEM(webHistoryItem));
383
384    g_return_val_if_fail(item, NULL);
385
386    WebKitWebHistoryItemPrivate* priv = webHistoryItem->priv;
387    priv->uri = item->urlString().utf8();
388
389    return priv->uri.data();
390}
391
392/**
393 * webkit_web_history_item_get_original_uri:
394 * @web_history_item: a #WebKitWebHistoryItem
395 *
396 * Returns the original URI of @web_history_item.
397 *
398 * Return value: the original URI of @web_history_item
399 */
400const gchar* webkit_web_history_item_get_original_uri(WebKitWebHistoryItem* webHistoryItem)
401{
402    g_return_val_if_fail(WEBKIT_IS_WEB_HISTORY_ITEM(webHistoryItem), NULL);
403
404    WebCore::HistoryItem* item = core(WEBKIT_WEB_HISTORY_ITEM(webHistoryItem));
405
406    g_return_val_if_fail(item, NULL);
407
408    WebKitWebHistoryItemPrivate* priv = webHistoryItem->priv;
409    priv->originalUri = item->originalURLString().utf8();
410
411    return webHistoryItem->priv->originalUri.data();
412}
413
414/**
415 * webkit_web_history_item_get_last_visisted_time :
416 * @web_history_item: a #WebKitWebHistoryItem
417 *
418 * Returns the last time @web_history_item was visited
419 *
420 * Return value: the time in seconds this @web_history_item was last visited
421 */
422gdouble webkit_web_history_item_get_last_visited_time(WebKitWebHistoryItem* webHistoryItem)
423{
424    g_return_val_if_fail(WEBKIT_IS_WEB_HISTORY_ITEM(webHistoryItem), 0);
425
426    WebCore::HistoryItem* item = core(WEBKIT_WEB_HISTORY_ITEM(webHistoryItem));
427
428    g_return_val_if_fail(item, 0);
429
430    return item->lastVisitedTime();
431}
432
433/**
434 * webkit_web_history_item_copy:
435 * @web_history_item: a #WebKitWebHistoryItem
436 *
437 * Makes a copy of the item for use with other WebView objects.
438 *
439 * Since: 1.1.18
440 *
441 * Return value: (transfer full): the new #WebKitWebHistoryItem.
442 */
443WebKitWebHistoryItem* webkit_web_history_item_copy(WebKitWebHistoryItem* self)
444{
445    WebKitWebHistoryItemPrivate* selfPrivate = self->priv;
446
447    WebKitWebHistoryItem* item = WEBKIT_WEB_HISTORY_ITEM(g_object_new(WEBKIT_TYPE_WEB_HISTORY_ITEM, 0));
448    WebKitWebHistoryItemPrivate* priv = item->priv;
449
450    priv->title = selfPrivate->title;
451    priv->alternateTitle = selfPrivate->alternateTitle;
452    priv->uri = selfPrivate->uri;
453    priv->originalUri = selfPrivate->originalUri;
454
455    priv->historyItem = selfPrivate->historyItem->copy().leakRef();
456
457    return item;
458}
459
460/* private methods */
461
462gchar* webkit_web_history_item_get_target(WebKitWebHistoryItem* webHistoryItem)
463{
464    g_return_val_if_fail(WEBKIT_IS_WEB_HISTORY_ITEM(webHistoryItem), NULL);
465
466    WebCore::HistoryItem* item = core(webHistoryItem);
467
468    g_return_val_if_fail(item, NULL);
469
470    WTF::CString t = item->target().utf8();
471    return g_strdup(t.data());
472}
473
474gboolean webkit_web_history_item_is_target_item(WebKitWebHistoryItem* webHistoryItem)
475{
476    g_return_val_if_fail(WEBKIT_IS_WEB_HISTORY_ITEM(webHistoryItem), false);
477
478    WebCore::HistoryItem* item = core(webHistoryItem);
479
480    g_return_val_if_fail(item, false);
481
482    return item->isTargetItem();
483}
484
485GList* webkit_web_history_item_get_children(WebKitWebHistoryItem* webHistoryItem)
486{
487    g_return_val_if_fail(WEBKIT_IS_WEB_HISTORY_ITEM(webHistoryItem), NULL);
488
489    WebCore::HistoryItem* item = core(webHistoryItem);
490
491    g_return_val_if_fail(item, NULL);
492
493    const WebCore::HistoryItemVector& children = item->children();
494    if (!children.size())
495        return NULL;
496
497    unsigned size = children.size();
498    GList* kids = NULL;
499    for (unsigned i = 0; i < size; ++i)
500        kids = g_list_prepend(kids, kit(children[i].get()));
501
502    return g_list_reverse(kids);
503}
504
505WebCore::HistoryItem* WebKit::core(WebKitWebHistoryItem* webHistoryItem)
506{
507    g_return_val_if_fail(WEBKIT_IS_WEB_HISTORY_ITEM(webHistoryItem), NULL);
508
509    return webHistoryItem->priv->historyItem;
510}
511
512WebKitWebHistoryItem* WebKit::kit(PassRefPtr<WebCore::HistoryItem> historyItem)
513{
514    g_return_val_if_fail(historyItem, NULL);
515
516    RefPtr<WebCore::HistoryItem> item = historyItem;
517    GHashTable* table = webkit_history_items();
518    WebKitWebHistoryItem* webHistoryItem = (WebKitWebHistoryItem*) g_hash_table_lookup(table, item.get());
519
520    if (!webHistoryItem) {
521        webHistoryItem = WEBKIT_WEB_HISTORY_ITEM(g_object_new(WEBKIT_TYPE_WEB_HISTORY_ITEM, NULL));
522        WebKitWebHistoryItemPrivate* priv = webHistoryItem->priv;
523
524        priv->historyItem = item.release().leakRef();
525        webkit_history_item_add(webHistoryItem, priv->historyItem);
526    }
527
528    return webHistoryItem;
529}
530
531