1 /*
2 * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved.
3 * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "config.h"
28 #include "BackForwardList.h"
29
30 #include "Frame.h"
31 #include "FrameLoader.h"
32 #include "FrameLoaderClient.h"
33 #include "HistoryItem.h"
34 #include "Logging.h"
35 #include "Page.h"
36 #include "PageCache.h"
37 #include "SerializedScriptValue.h"
38
39 using namespace std;
40
41 namespace WebCore {
42
43 static const unsigned DefaultCapacity = 100;
44 static const unsigned NoCurrentItemIndex = UINT_MAX;
45
BackForwardList(Page * page)46 BackForwardList::BackForwardList(Page* page)
47 : m_page(page)
48 , m_current(NoCurrentItemIndex)
49 , m_capacity(DefaultCapacity)
50 , m_closed(true)
51 , m_enabled(true)
52 {
53 }
54
~BackForwardList()55 BackForwardList::~BackForwardList()
56 {
57 ASSERT(m_closed);
58 }
59
addItem(PassRefPtr<HistoryItem> prpItem)60 void BackForwardList::addItem(PassRefPtr<HistoryItem> prpItem)
61 {
62 ASSERT(prpItem);
63 if (m_capacity == 0 || !m_enabled)
64 return;
65
66 // Toss anything in the forward list
67 if (m_current != NoCurrentItemIndex) {
68 unsigned targetSize = m_current + 1;
69 while (m_entries.size() > targetSize) {
70 RefPtr<HistoryItem> item = m_entries.last();
71 m_entries.removeLast();
72 m_entryHash.remove(item);
73 pageCache()->remove(item.get());
74 }
75 }
76
77 // Toss the first item if the list is getting too big, as long as we're not using it
78 // (or even if we are, if we only want 1 entry).
79 if (m_entries.size() == m_capacity && (m_current != 0 || m_capacity == 1)) {
80 RefPtr<HistoryItem> item = m_entries[0];
81 m_entries.remove(0);
82 m_entryHash.remove(item);
83 pageCache()->remove(item.get());
84 m_current--;
85 m_page->mainFrame()->loader()->client()->dispatchDidRemoveBackForwardItem(item.get());
86 }
87
88 m_entryHash.add(prpItem.get());
89 m_entries.insert(m_current + 1, prpItem);
90 m_current++;
91 m_page->mainFrame()->loader()->client()->dispatchDidAddBackForwardItem(currentItem());
92 }
93
goBack()94 void BackForwardList::goBack()
95 {
96 ASSERT(m_current > 0);
97 if (m_current > 0) {
98 m_current--;
99 m_page->mainFrame()->loader()->client()->dispatchDidChangeBackForwardIndex();
100 }
101 }
102
goForward()103 void BackForwardList::goForward()
104 {
105 ASSERT(m_current < m_entries.size() - 1);
106 if (m_current < m_entries.size() - 1) {
107 m_current++;
108 m_page->mainFrame()->loader()->client()->dispatchDidChangeBackForwardIndex();
109 }
110 }
111
goToItem(HistoryItem * item)112 void BackForwardList::goToItem(HistoryItem* item)
113 {
114 if (!m_entries.size() || !item)
115 return;
116
117 unsigned int index = 0;
118 for (; index < m_entries.size(); ++index)
119 if (m_entries[index] == item)
120 break;
121 if (index < m_entries.size()) {
122 m_current = index;
123 m_page->mainFrame()->loader()->client()->dispatchDidChangeBackForwardIndex();
124 }
125 }
126
backItem()127 HistoryItem* BackForwardList::backItem()
128 {
129 if (m_current && m_current != NoCurrentItemIndex)
130 return m_entries[m_current - 1].get();
131 return 0;
132 }
133
currentItem()134 HistoryItem* BackForwardList::currentItem()
135 {
136 if (m_current != NoCurrentItemIndex)
137 return m_entries[m_current].get();
138 return 0;
139 }
140
forwardItem()141 HistoryItem* BackForwardList::forwardItem()
142 {
143 if (m_entries.size() && m_current < m_entries.size() - 1)
144 return m_entries[m_current + 1].get();
145 return 0;
146 }
147
backListWithLimit(int limit,HistoryItemVector & list)148 void BackForwardList::backListWithLimit(int limit, HistoryItemVector& list)
149 {
150 list.clear();
151 if (m_current != NoCurrentItemIndex) {
152 unsigned first = max((int)m_current - limit, 0);
153 for (; first < m_current; ++first)
154 list.append(m_entries[first]);
155 }
156 }
157
forwardListWithLimit(int limit,HistoryItemVector & list)158 void BackForwardList::forwardListWithLimit(int limit, HistoryItemVector& list)
159 {
160 ASSERT(limit > -1);
161 list.clear();
162 if (!m_entries.size())
163 return;
164
165 unsigned lastEntry = m_entries.size() - 1;
166 if (m_current < lastEntry) {
167 int last = min(m_current + limit, lastEntry);
168 limit = m_current + 1;
169 for (; limit <= last; ++limit)
170 list.append(m_entries[limit]);
171 }
172 }
173
capacity()174 int BackForwardList::capacity()
175 {
176 return m_capacity;
177 }
178
setCapacity(int size)179 void BackForwardList::setCapacity(int size)
180 {
181 while (size < (int)m_entries.size()) {
182 RefPtr<HistoryItem> item = m_entries.last();
183 m_entries.removeLast();
184 m_entryHash.remove(item);
185 pageCache()->remove(item.get());
186 }
187
188 if (!size)
189 m_current = NoCurrentItemIndex;
190 else if (m_current > m_entries.size() - 1) {
191 m_current = m_entries.size() - 1;
192 m_page->mainFrame()->loader()->client()->dispatchDidChangeBackForwardIndex();
193 }
194 m_capacity = size;
195 }
196
enabled()197 bool BackForwardList::enabled()
198 {
199 return m_enabled;
200 }
201
setEnabled(bool enabled)202 void BackForwardList::setEnabled(bool enabled)
203 {
204 m_enabled = enabled;
205 if (!enabled) {
206 int capacity = m_capacity;
207 setCapacity(0);
208 setCapacity(capacity);
209 }
210 }
211
backListCount()212 int BackForwardList::backListCount()
213 {
214 return m_current == NoCurrentItemIndex ? 0 : m_current;
215 }
216
forwardListCount()217 int BackForwardList::forwardListCount()
218 {
219 return m_current == NoCurrentItemIndex ? 0 : (int)m_entries.size() - (m_current + 1);
220 }
221
itemAtIndex(int index)222 HistoryItem* BackForwardList::itemAtIndex(int index)
223 {
224 // Do range checks without doing math on index to avoid overflow.
225 if (index < -(int)m_current)
226 return 0;
227
228 if (index > forwardListCount())
229 return 0;
230
231 return m_entries[index + m_current].get();
232 }
233
entries()234 HistoryItemVector& BackForwardList::entries()
235 {
236 return m_entries;
237 }
238
pushStateItem(PassRefPtr<HistoryItem> newItem)239 void BackForwardList::pushStateItem(PassRefPtr<HistoryItem> newItem)
240 {
241 ASSERT(newItem);
242 ASSERT(newItem->stateObject());
243
244 RefPtr<HistoryItem> current = currentItem();
245 ASSERT(current);
246
247 addItem(newItem);
248
249 if (!current->stateObject())
250 current->setStateObject(SerializedScriptValue::create());
251 }
252
close()253 void BackForwardList::close()
254 {
255 int size = m_entries.size();
256 for (int i = 0; i < size; ++i)
257 pageCache()->remove(m_entries[i].get());
258 m_entries.clear();
259 m_entryHash.clear();
260 m_page = 0;
261 m_closed = true;
262 }
263
closed()264 bool BackForwardList::closed()
265 {
266 return m_closed;
267 }
268
removeItem(HistoryItem * item)269 void BackForwardList::removeItem(HistoryItem* item)
270 {
271 if (!item)
272 return;
273
274 for (unsigned i = 0; i < m_entries.size(); ++i)
275 if (m_entries[i] == item) {
276 m_entries.remove(i);
277 m_entryHash.remove(item);
278 if (m_current == NoCurrentItemIndex || m_current < i)
279 break;
280 if (m_current > i)
281 m_current--;
282 else {
283 size_t count = m_entries.size();
284 if (m_current >= count)
285 m_current = count ? count - 1 : NoCurrentItemIndex;
286 }
287 break;
288 }
289 }
290
containsItem(HistoryItem * entry)291 bool BackForwardList::containsItem(HistoryItem* entry)
292 {
293 return m_entryHash.contains(entry);
294 }
295
296 #if ENABLE(WML)
clearWMLPageHistory()297 void BackForwardList::clearWMLPageHistory()
298 {
299 RefPtr<HistoryItem> currentItem = this->currentItem();
300
301 int size = m_entries.size();
302 for (int i = 0; i < size; ++i)
303 pageCache()->remove(m_entries[i].get());
304
305 m_entries.clear();
306 m_entryHash.clear();
307 m_current = NoCurrentItemIndex;
308
309 // Spec: The history stack may be reset to a state where it only contains the current card.
310 addItem(currentItem);
311 }
312 #endif
313
314 }; // namespace WebCore
315