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 "APIArray.h" |
24 | #include "WebKitBackForwardListPrivate.h" |
25 | #include <wtf/glib/GRefPtr.h> |
26 | #include <wtf/glib/WTFGType.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 | |
49 | using namespace WebKit; |
50 | |
51 | enum { |
52 | CHANGED, |
53 | |
54 | LAST_SIGNAL |
55 | }; |
56 | |
57 | typedef HashMap<WebBackForwardListItem*, GRefPtr<WebKitBackForwardListItem> > BackForwardListItemsMap; |
58 | |
59 | struct _WebKitBackForwardListPrivate { |
60 | WebBackForwardList* backForwardItems; |
61 | BackForwardListItemsMap itemsMap; |
62 | }; |
63 | |
64 | static guint signals[LAST_SIGNAL] = { 0, }; |
65 | |
66 | WEBKIT_DEFINE_TYPE(WebKitBackForwardList, webkit_back_forward_list, G_TYPE_OBJECT) |
67 | |
68 | static 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] = g_signal_new( |
83 | "changed" , |
84 | G_TYPE_FROM_CLASS(listClass), |
85 | G_SIGNAL_RUN_LAST, |
86 | 0, nullptr, nullptr, |
87 | g_cclosure_marshal_generic, |
88 | G_TYPE_NONE, 2, |
89 | WEBKIT_TYPE_BACK_FORWARD_LIST_ITEM, |
90 | G_TYPE_POINTER); |
91 | } |
92 | |
93 | static 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 | |
109 | static GList* webkitBackForwardListCreateList(WebKitBackForwardList* list, API::Array* 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 | |
123 | WebKitBackForwardList* 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 | |
131 | void webkitBackForwardListChanged(WebKitBackForwardList* backForwardList, WebBackForwardListItem* webAddedItem, const Vector<Ref<WebBackForwardListItem>>& webRemovedItems) |
132 | { |
133 | WebKitBackForwardListItem* addedItem = webkitBackForwardListGetOrCreateItem(backForwardList, webAddedItem); |
134 | GList* removedItems = nullptr; |
135 | |
136 | WebKitBackForwardListPrivate* priv = backForwardList->priv; |
137 | for (auto& webItem : webRemovedItems) { |
138 | // After a session restore, we still don't have wrappers for the newly added items, so it would be possible that |
139 | // the removed items are not in the map. In that case we create a wrapper now to pass it the changed signal, but |
140 | // without adding it to the item map. See https://bugs.webkit.org/show_bug.cgi?id=153233. |
141 | GRefPtr<WebKitBackForwardListItem> removedItem = priv->itemsMap.get(webItem.ptr()); |
142 | if (removedItem) { |
143 | removedItems = g_list_prepend(removedItems, g_object_ref(removedItem.get())); |
144 | priv->itemsMap.remove(webItem.ptr()); |
145 | } else |
146 | removedItems = g_list_prepend(removedItems, webkitBackForwardListItemGetOrCreate(webItem.ptr())); |
147 | } |
148 | |
149 | g_signal_emit(backForwardList, signals[CHANGED], 0, addedItem, removedItems, nullptr); |
150 | g_list_free_full(removedItems, static_cast<GDestroyNotify>(g_object_unref)); |
151 | } |
152 | |
153 | /** |
154 | * webkit_back_forward_list_get_current_item: |
155 | * @back_forward_list: a #WebKitBackForwardList |
156 | * |
157 | * Returns the current item in @back_forward_list. |
158 | * |
159 | * Returns: (nullable) (transfer none): a #WebKitBackForwardListItem |
160 | * or %NULL if @back_forward_list is empty. |
161 | */ |
162 | WebKitBackForwardListItem* webkit_back_forward_list_get_current_item(WebKitBackForwardList* backForwardList) |
163 | { |
164 | g_return_val_if_fail(WEBKIT_IS_BACK_FORWARD_LIST(backForwardList), 0); |
165 | |
166 | return webkitBackForwardListGetOrCreateItem(backForwardList, backForwardList->priv->backForwardItems->currentItem()); |
167 | } |
168 | |
169 | /** |
170 | * webkit_back_forward_list_get_back_item: |
171 | * @back_forward_list: a #WebKitBackForwardList |
172 | * |
173 | * Returns the item that precedes the current item. |
174 | * |
175 | * Returns: (nullable) (transfer none): the #WebKitBackForwardListItem |
176 | * preceding the current item or %NULL. |
177 | */ |
178 | WebKitBackForwardListItem* webkit_back_forward_list_get_back_item(WebKitBackForwardList* backForwardList) |
179 | { |
180 | g_return_val_if_fail(WEBKIT_IS_BACK_FORWARD_LIST(backForwardList), 0); |
181 | |
182 | return webkitBackForwardListGetOrCreateItem(backForwardList, backForwardList->priv->backForwardItems->backItem()); |
183 | } |
184 | |
185 | /** |
186 | * webkit_back_forward_list_get_forward_item: |
187 | * @back_forward_list: a #WebKitBackForwardList |
188 | * |
189 | * Returns the item that follows the current item. |
190 | * |
191 | * Returns: (nullable) (transfer none): the #WebKitBackForwardListItem |
192 | * following the current item or %NULL. |
193 | */ |
194 | WebKitBackForwardListItem* webkit_back_forward_list_get_forward_item(WebKitBackForwardList* backForwardList) |
195 | { |
196 | g_return_val_if_fail(WEBKIT_IS_BACK_FORWARD_LIST(backForwardList), 0); |
197 | |
198 | return webkitBackForwardListGetOrCreateItem(backForwardList, backForwardList->priv->backForwardItems->forwardItem()); |
199 | } |
200 | |
201 | /** |
202 | * webkit_back_forward_list_get_nth_item: |
203 | * @back_forward_list: a #WebKitBackForwardList |
204 | * @index: the index of the item |
205 | * |
206 | * Returns the item at a given index relative to the current item. |
207 | * |
208 | * Returns: (nullable) (transfer none): the #WebKitBackForwardListItem |
209 | * located at the specified index relative to the current item or %NULL. |
210 | */ |
211 | WebKitBackForwardListItem* webkit_back_forward_list_get_nth_item(WebKitBackForwardList* backForwardList, gint index) |
212 | { |
213 | g_return_val_if_fail(WEBKIT_IS_BACK_FORWARD_LIST(backForwardList), 0); |
214 | |
215 | return webkitBackForwardListGetOrCreateItem(backForwardList, backForwardList->priv->backForwardItems->itemAtIndex(index)); |
216 | } |
217 | |
218 | /** |
219 | * webkit_back_forward_list_get_length: |
220 | * @back_forward_list: a #WebKitBackForwardList |
221 | * |
222 | * Returns: the length of @back_forward_list. |
223 | */ |
224 | guint webkit_back_forward_list_get_length(WebKitBackForwardList* backForwardList) |
225 | { |
226 | g_return_val_if_fail(WEBKIT_IS_BACK_FORWARD_LIST(backForwardList), 0); |
227 | |
228 | WebKitBackForwardListPrivate* priv = backForwardList->priv; |
229 | guint currentItem = webkit_back_forward_list_get_current_item(backForwardList) ? 1 : 0; |
230 | return priv->backForwardItems->backListCount() + priv->backForwardItems->forwardListCount() + currentItem; |
231 | } |
232 | |
233 | /** |
234 | * webkit_back_forward_list_get_back_list: |
235 | * @back_forward_list: a #WebKitBackForwardList |
236 | * |
237 | * Returns: (element-type WebKit2.BackForwardListItem) (transfer container): a #GList of |
238 | * items preceding the current item. |
239 | */ |
240 | GList* webkit_back_forward_list_get_back_list(WebKitBackForwardList* backForwardList) |
241 | { |
242 | g_return_val_if_fail(WEBKIT_IS_BACK_FORWARD_LIST(backForwardList), 0); |
243 | |
244 | return webkit_back_forward_list_get_back_list_with_limit(backForwardList, backForwardList->priv->backForwardItems->backListCount()); |
245 | } |
246 | |
247 | /** |
248 | * webkit_back_forward_list_get_back_list_with_limit: |
249 | * @back_forward_list: a #WebKitBackForwardList |
250 | * @limit: the number of items to retrieve |
251 | * |
252 | * Returns: (element-type WebKit2.BackForwardListItem) (transfer container): a #GList of |
253 | * items preceding the current item limited by @limit. |
254 | */ |
255 | GList* webkit_back_forward_list_get_back_list_with_limit(WebKitBackForwardList* backForwardList, guint limit) |
256 | { |
257 | g_return_val_if_fail(WEBKIT_IS_BACK_FORWARD_LIST(backForwardList), 0); |
258 | |
259 | WebKitBackForwardListPrivate* priv = backForwardList->priv; |
260 | Ref<API::Array> apiArray = priv->backForwardItems->backListAsAPIArrayWithLimit(limit); |
261 | return webkitBackForwardListCreateList(backForwardList, apiArray.ptr()); |
262 | } |
263 | |
264 | /** |
265 | * webkit_back_forward_list_get_forward_list: |
266 | * @back_forward_list: a #WebKitBackForwardList |
267 | * |
268 | * Returns: (element-type WebKit2.BackForwardListItem) (transfer container): a #GList of |
269 | * items following the current item. |
270 | */ |
271 | GList* webkit_back_forward_list_get_forward_list(WebKitBackForwardList* backForwardList) |
272 | { |
273 | g_return_val_if_fail(WEBKIT_IS_BACK_FORWARD_LIST(backForwardList), 0); |
274 | |
275 | return webkit_back_forward_list_get_forward_list_with_limit(backForwardList, backForwardList->priv->backForwardItems->forwardListCount()); |
276 | } |
277 | |
278 | /** |
279 | * webkit_back_forward_list_get_forward_list_with_limit: |
280 | * @back_forward_list: a #WebKitBackForwardList |
281 | * @limit: the number of items to retrieve |
282 | * |
283 | * Returns: (element-type WebKit2.BackForwardListItem) (transfer container): a #GList of |
284 | * items following the current item limited by @limit. |
285 | */ |
286 | GList* webkit_back_forward_list_get_forward_list_with_limit(WebKitBackForwardList* backForwardList, guint limit) |
287 | { |
288 | g_return_val_if_fail(WEBKIT_IS_BACK_FORWARD_LIST(backForwardList), 0); |
289 | |
290 | WebKitBackForwardListPrivate* priv = backForwardList->priv; |
291 | Ref<API::Array> apiArray = priv->backForwardItems->forwardListAsAPIArrayWithLimit(limit); |
292 | return webkitBackForwardListCreateList(backForwardList, apiArray.ptr()); |
293 | } |
294 | |