1/*
2 * Copyright (C) 2011 Igalia S.L.
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 "WebKitBackForwardList.h"
22
23#include "WebKitBackForwardListPrivate.h"
24#include "WebKitMarshal.h"
25#include "WebKitPrivate.h"
26#include <wtf/gobject/GRefPtr.h>
27
28/**
29 * SECTION: WebKitBackForwardList
30 * @Short_description: List of visited pages
31 * @Title: WebKitBackForwardList
32 * @See_also: #WebKitWebView, #WebKitBackForwardListItem
33 *
34 * WebKitBackForwardList maintains a list of visited pages used to
35 * navigate to recent pages. Items are inserted in the list in the
36 * order they are visited.
37 *
38 * WebKitBackForwardList also maintains the notion of the current item
39 * (which is always at index 0), the preceding item (which is at index -1),
40 * and the following item (which is at index 1).
41 * Methods webkit_web_view_go_back() and webkit_web_view_go_forward() move
42 * the current item backward or forward by one. Method
43 * webkit_web_view_go_to_back_forward_list_item() sets the current item to the
44 * specified item. All other methods returning #WebKitBackForwardListItem<!-- -->s
45 * do not change the value of the current item, they just return the requested
46 * item or items.
47 */
48
49using namespace WebKit;
50
51enum {
52    CHANGED,
53
54    LAST_SIGNAL
55};
56
57typedef HashMap<WebBackForwardListItem*, GRefPtr<WebKitBackForwardListItem> > BackForwardListItemsMap;
58
59struct _WebKitBackForwardListPrivate {
60    WebBackForwardList* backForwardItems;
61    BackForwardListItemsMap itemsMap;
62};
63
64static guint signals[LAST_SIGNAL] = { 0, };
65
66WEBKIT_DEFINE_TYPE(WebKitBackForwardList, webkit_back_forward_list, G_TYPE_OBJECT)
67
68static void webkit_back_forward_list_class_init(WebKitBackForwardListClass* listClass)
69{
70    /**
71     * WebKitBackForwardList::changed:
72     * @back_forward_list: the #WebKitBackForwardList on which the signal was emitted
73     * @item_added: (allow-none): the #WebKitBackForwardListItem added or %NULL
74     * @items_removed: a #GList of #WebKitBackForwardListItem<!-- -->s
75     *
76     * This signal is emitted when @back_forward_list changes. This happens
77     * when the current item is updated, a new item is added or one or more
78     * items are removed. Note that both @item_added and @items_removed can
79     * %NULL when only the current item is updated. Items are only removed
80     * when the list is cleared or the maximum items limit is reached.
81     */
82    signals[CHANGED] =
83        g_signal_new("changed",
84                     G_TYPE_FROM_CLASS(listClass),
85                     G_SIGNAL_RUN_LAST,
86                     0, 0, 0,
87                     webkit_marshal_VOID__OBJECT_POINTER,
88                     G_TYPE_NONE, 2,
89                     WEBKIT_TYPE_BACK_FORWARD_LIST_ITEM,
90                     G_TYPE_POINTER);
91}
92
93static WebKitBackForwardListItem* webkitBackForwardListGetOrCreateItem(WebKitBackForwardList* list, WebBackForwardListItem* webListItem)
94{
95    if (!webListItem)
96        return 0;
97
98    WebKitBackForwardListPrivate* priv = list->priv;
99    GRefPtr<WebKitBackForwardListItem> listItem = priv->itemsMap.get(webListItem);
100    if (listItem)
101        return listItem.get();
102
103    listItem = webkitBackForwardListItemGetOrCreate(webListItem);
104    priv->itemsMap.set(webListItem, listItem);
105
106    return listItem.get();
107}
108
109static GList* webkitBackForwardListCreateList(WebKitBackForwardList* list, ImmutableArray* backForwardItems)
110{
111    if (!backForwardItems)
112        return 0;
113
114    GList* returnValue = 0;
115    for (size_t i = 0; i < backForwardItems->size(); ++i) {
116        WebBackForwardListItem* webItem = static_cast<WebBackForwardListItem*>(backForwardItems->at(i));
117        returnValue = g_list_prepend(returnValue, webkitBackForwardListGetOrCreateItem(list, webItem));
118    }
119
120    return returnValue;
121}
122
123WebKitBackForwardList* webkitBackForwardListCreate(WebBackForwardList* backForwardItems)
124{
125    WebKitBackForwardList* list = WEBKIT_BACK_FORWARD_LIST(g_object_new(WEBKIT_TYPE_BACK_FORWARD_LIST, NULL));
126    list->priv->backForwardItems = backForwardItems;
127
128    return list;
129}
130
131void webkitBackForwardListChanged(WebKitBackForwardList* backForwardList, WebBackForwardListItem* webAddedItem, ImmutableArray* webRemovedItems)
132{
133    WebKitBackForwardListItem* addedItem = webkitBackForwardListGetOrCreateItem(backForwardList, webAddedItem);
134    GList* removedItems = 0;
135
136    size_t removedItemsSize = webRemovedItems ? webRemovedItems->size() : 0;
137    WebKitBackForwardListPrivate* priv = backForwardList->priv;
138    for (size_t i = 0; i < removedItemsSize; ++i) {
139        WebBackForwardListItem* webItem = static_cast<WebBackForwardListItem*>(webRemovedItems->at(i));
140        removedItems = g_list_prepend(removedItems, g_object_ref(G_OBJECT(priv->itemsMap.get(webItem).get())));
141        priv->itemsMap.remove(webItem);
142    }
143
144    g_signal_emit(backForwardList, signals[CHANGED], 0, addedItem, removedItems, NULL);
145    g_list_free_full(removedItems, static_cast<GDestroyNotify>(g_object_unref));
146}
147
148/**
149 * webkit_back_forward_list_get_current_item:
150 * @back_forward_list: a #WebKitBackForwardList
151 *
152 * Returns the current item in @back_forward_list.
153 *
154 * Returns: (transfer none): a #WebKitBackForwardListItem
155 *    or %NULL if @back_forward_list is empty.
156 */
157WebKitBackForwardListItem* webkit_back_forward_list_get_current_item(WebKitBackForwardList* backForwardList)
158{
159    g_return_val_if_fail(WEBKIT_IS_BACK_FORWARD_LIST(backForwardList), 0);
160
161    return webkitBackForwardListGetOrCreateItem(backForwardList, backForwardList->priv->backForwardItems->currentItem());
162}
163
164/**
165 * webkit_back_forward_list_get_back_item:
166 * @back_forward_list: a #WebKitBackForwardList
167 *
168 * Returns the item that precedes the current item.
169 *
170 * Returns: (transfer none): the #WebKitBackForwardListItem
171 *    preceding the current item or %NULL.
172 */
173WebKitBackForwardListItem* webkit_back_forward_list_get_back_item(WebKitBackForwardList* backForwardList)
174{
175    g_return_val_if_fail(WEBKIT_IS_BACK_FORWARD_LIST(backForwardList), 0);
176
177    return webkitBackForwardListGetOrCreateItem(backForwardList, backForwardList->priv->backForwardItems->backItem());
178}
179
180/**
181 * webkit_back_forward_list_get_forward_item:
182 * @back_forward_list: a #WebKitBackForwardList
183 *
184 * Returns the item that follows the current item.
185 *
186 * Returns: (transfer none): the #WebKitBackForwardListItem
187 *    following the current item or %NULL.
188 */
189WebKitBackForwardListItem* webkit_back_forward_list_get_forward_item(WebKitBackForwardList* backForwardList)
190{
191    g_return_val_if_fail(WEBKIT_IS_BACK_FORWARD_LIST(backForwardList), 0);
192
193    return webkitBackForwardListGetOrCreateItem(backForwardList, backForwardList->priv->backForwardItems->forwardItem());
194}
195
196/**
197 * webkit_back_forward_list_get_nth_item:
198 * @back_forward_list: a #WebKitBackForwardList
199 * @index: the index of the item
200 *
201 * Returns the item at a given index relative to the current item.
202 *
203 * Returns: (transfer none): the #WebKitBackForwardListItem
204 *    located at the specified index relative to the current item.
205 */
206WebKitBackForwardListItem* webkit_back_forward_list_get_nth_item(WebKitBackForwardList* backForwardList, gint index)
207{
208    g_return_val_if_fail(WEBKIT_IS_BACK_FORWARD_LIST(backForwardList), 0);
209
210    return webkitBackForwardListGetOrCreateItem(backForwardList, backForwardList->priv->backForwardItems->itemAtIndex(index));
211}
212
213/**
214 * webkit_back_forward_list_get_length:
215 * @back_forward_list: a #WebKitBackForwardList
216 *
217 * Returns: the length of @back_forward_list.
218 */
219guint webkit_back_forward_list_get_length(WebKitBackForwardList* backForwardList)
220{
221    g_return_val_if_fail(WEBKIT_IS_BACK_FORWARD_LIST(backForwardList), 0);
222
223    WebKitBackForwardListPrivate* priv = backForwardList->priv;
224    guint currentItem = webkit_back_forward_list_get_current_item(backForwardList) ? 1 : 0;
225    return priv->backForwardItems->backListCount() + priv->backForwardItems->forwardListCount() + currentItem;
226}
227
228/**
229 * webkit_back_forward_list_get_back_list:
230 * @back_forward_list: a #WebKitBackForwardList
231 *
232 * Returns: (element-type WebKit2.BackForwardListItem) (transfer container): a #GList of
233 *    items preceding the current item.
234 */
235GList* webkit_back_forward_list_get_back_list(WebKitBackForwardList* backForwardList)
236{
237    g_return_val_if_fail(WEBKIT_IS_BACK_FORWARD_LIST(backForwardList), 0);
238
239    return webkit_back_forward_list_get_back_list_with_limit(backForwardList, backForwardList->priv->backForwardItems->backListCount());
240}
241
242/**
243 * webkit_back_forward_list_get_back_list_with_limit:
244 * @back_forward_list: a #WebKitBackForwardList
245 * @limit: the number of items to retrieve
246 *
247 * Returns: (element-type WebKit2.BackForwardListItem) (transfer container): a #GList of
248 *    items preceding the current item limited by @limit.
249 */
250GList* webkit_back_forward_list_get_back_list_with_limit(WebKitBackForwardList* backForwardList, guint limit)
251{
252    g_return_val_if_fail(WEBKIT_IS_BACK_FORWARD_LIST(backForwardList), 0);
253
254    WebKitBackForwardListPrivate* priv = backForwardList->priv;
255    RefPtr<ImmutableArray> immutableArray = priv->backForwardItems->backListAsImmutableArrayWithLimit(limit);
256    return webkitBackForwardListCreateList(backForwardList, immutableArray.get());
257}
258
259/**
260 * webkit_back_forward_list_get_forward_list:
261 * @back_forward_list: a #WebKitBackForwardList
262 *
263 * Returns: (element-type WebKit2.BackForwardListItem) (transfer container): a #GList of
264 *    items following the current item.
265 */
266GList* webkit_back_forward_list_get_forward_list(WebKitBackForwardList* backForwardList)
267{
268    g_return_val_if_fail(WEBKIT_IS_BACK_FORWARD_LIST(backForwardList), 0);
269
270    return webkit_back_forward_list_get_forward_list_with_limit(backForwardList, backForwardList->priv->backForwardItems->forwardListCount());
271}
272
273/**
274 * webkit_back_forward_list_get_forward_list_with_limit:
275 * @back_forward_list: a #WebKitBackForwardList
276 * @limit: the number of items to retrieve
277 *
278 * Returns: (element-type WebKit2.BackForwardListItem) (transfer container): a #GList of
279 *    items following the current item limited by @limit.
280 */
281GList* webkit_back_forward_list_get_forward_list_with_limit(WebKitBackForwardList* backForwardList, guint limit)
282{
283    g_return_val_if_fail(WEBKIT_IS_BACK_FORWARD_LIST(backForwardList), 0);
284
285    WebKitBackForwardListPrivate* priv = backForwardList->priv;
286    RefPtr<ImmutableArray> immutableArray = priv->backForwardItems->forwardListAsImmutableArrayWithLimit(limit);
287    return webkitBackForwardListCreateList(backForwardList, immutableArray.get());
288}
289