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 "BackForwardListImpl.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
BackForwardListImpl(Page * page)46 BackForwardListImpl::BackForwardListImpl(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
~BackForwardListImpl()55 BackForwardListImpl::~BackForwardListImpl()
56 {
57 ASSERT(m_closed);
58 }
59
addItem(PassRefPtr<HistoryItem> prpItem)60 void BackForwardListImpl::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 if (m_page)
86 m_page->mainFrame()->loader()->client()->dispatchDidRemoveBackForwardItem(item.get());
87 }
88
89 m_entryHash.add(prpItem.get());
90 m_entries.insert(m_current + 1, prpItem);
91 m_current++;
92 if (m_page)
93 m_page->mainFrame()->loader()->client()->dispatchDidAddBackForwardItem(currentItem());
94 }
95
goBack()96 void BackForwardListImpl::goBack()
97 {
98 ASSERT(m_current > 0);
99 if (m_current > 0) {
100 m_current--;
101 if (m_page)
102 m_page->mainFrame()->loader()->client()->dispatchDidChangeBackForwardIndex();
103 }
104 }
105
goForward()106 void BackForwardListImpl::goForward()
107 {
108 ASSERT(m_current < m_entries.size() - 1);
109 if (m_current < m_entries.size() - 1) {
110 m_current++;
111 if (m_page)
112 m_page->mainFrame()->loader()->client()->dispatchDidChangeBackForwardIndex();
113 }
114 }
115
goToItem(HistoryItem * item)116 void BackForwardListImpl::goToItem(HistoryItem* item)
117 {
118 if (!m_entries.size() || !item)
119 return;
120
121 unsigned int index = 0;
122 for (; index < m_entries.size(); ++index)
123 if (m_entries[index] == item)
124 break;
125 if (index < m_entries.size()) {
126 m_current = index;
127 if (m_page)
128 m_page->mainFrame()->loader()->client()->dispatchDidChangeBackForwardIndex();
129 }
130 }
131
backItem()132 HistoryItem* BackForwardListImpl::backItem()
133 {
134 if (m_current && m_current != NoCurrentItemIndex)
135 return m_entries[m_current - 1].get();
136 return 0;
137 }
138
currentItem()139 HistoryItem* BackForwardListImpl::currentItem()
140 {
141 if (m_current != NoCurrentItemIndex)
142 return m_entries[m_current].get();
143 return 0;
144 }
145
forwardItem()146 HistoryItem* BackForwardListImpl::forwardItem()
147 {
148 if (m_entries.size() && m_current < m_entries.size() - 1)
149 return m_entries[m_current + 1].get();
150 return 0;
151 }
152
backListWithLimit(int limit,HistoryItemVector & list)153 void BackForwardListImpl::backListWithLimit(int limit, HistoryItemVector& list)
154 {
155 list.clear();
156 if (m_current != NoCurrentItemIndex) {
157 unsigned first = max((int)m_current - limit, 0);
158 for (; first < m_current; ++first)
159 list.append(m_entries[first]);
160 }
161 }
162
forwardListWithLimit(int limit,HistoryItemVector & list)163 void BackForwardListImpl::forwardListWithLimit(int limit, HistoryItemVector& list)
164 {
165 ASSERT(limit > -1);
166 list.clear();
167 if (!m_entries.size())
168 return;
169
170 unsigned lastEntry = m_entries.size() - 1;
171 if (m_current < lastEntry) {
172 int last = min(m_current + limit, lastEntry);
173 limit = m_current + 1;
174 for (; limit <= last; ++limit)
175 list.append(m_entries[limit]);
176 }
177 }
178
capacity()179 int BackForwardListImpl::capacity()
180 {
181 return m_capacity;
182 }
183
setCapacity(int size)184 void BackForwardListImpl::setCapacity(int size)
185 {
186 while (size < (int)m_entries.size()) {
187 RefPtr<HistoryItem> item = m_entries.last();
188 m_entries.removeLast();
189 m_entryHash.remove(item);
190 pageCache()->remove(item.get());
191 }
192
193 if (!size)
194 m_current = NoCurrentItemIndex;
195 else if (m_current > m_entries.size() - 1) {
196 m_current = m_entries.size() - 1;
197 if (m_page)
198 m_page->mainFrame()->loader()->client()->dispatchDidChangeBackForwardIndex();
199 }
200 m_capacity = size;
201 }
202
enabled()203 bool BackForwardListImpl::enabled()
204 {
205 return m_enabled;
206 }
207
setEnabled(bool enabled)208 void BackForwardListImpl::setEnabled(bool enabled)
209 {
210 m_enabled = enabled;
211 if (!enabled) {
212 int capacity = m_capacity;
213 setCapacity(0);
214 setCapacity(capacity);
215 }
216 }
217
backListCount()218 int BackForwardListImpl::backListCount()
219 {
220 return m_current == NoCurrentItemIndex ? 0 : m_current;
221 }
222
forwardListCount()223 int BackForwardListImpl::forwardListCount()
224 {
225 return m_current == NoCurrentItemIndex ? 0 : (int)m_entries.size() - (m_current + 1);
226 }
227
itemAtIndex(int index)228 HistoryItem* BackForwardListImpl::itemAtIndex(int index)
229 {
230 // Do range checks without doing math on index to avoid overflow.
231 if (index < -(int)m_current)
232 return 0;
233
234 if (index > forwardListCount())
235 return 0;
236
237 return m_entries[index + m_current].get();
238 }
239
entries()240 HistoryItemVector& BackForwardListImpl::entries()
241 {
242 return m_entries;
243 }
244
close()245 void BackForwardListImpl::close()
246 {
247 int size = m_entries.size();
248 for (int i = 0; i < size; ++i)
249 pageCache()->remove(m_entries[i].get());
250 m_entries.clear();
251 m_entryHash.clear();
252 m_page = 0;
253 m_closed = true;
254 }
255
closed()256 bool BackForwardListImpl::closed()
257 {
258 return m_closed;
259 }
260
removeItem(HistoryItem * item)261 void BackForwardListImpl::removeItem(HistoryItem* item)
262 {
263 if (!item)
264 return;
265
266 for (unsigned i = 0; i < m_entries.size(); ++i)
267 if (m_entries[i] == item) {
268 m_entries.remove(i);
269 m_entryHash.remove(item);
270 if (m_current == NoCurrentItemIndex || m_current < i)
271 break;
272 if (m_current > i)
273 m_current--;
274 else {
275 size_t count = m_entries.size();
276 if (m_current >= count)
277 m_current = count ? count - 1 : NoCurrentItemIndex;
278 }
279 break;
280 }
281 }
282
containsItem(HistoryItem * entry)283 bool BackForwardListImpl::containsItem(HistoryItem* entry)
284 {
285 return m_entryHash.contains(entry);
286 }
287
288 #if ENABLE(WML)
clearWMLPageHistory()289 void BackForwardListImpl::clearWMLPageHistory()
290 {
291 RefPtr<HistoryItem> currentItem = this->currentItem();
292
293 int size = m_entries.size();
294 for (int i = 0; i < size; ++i)
295 pageCache()->remove(m_entries[i].get());
296
297 m_entries.clear();
298 m_entryHash.clear();
299 m_current = NoCurrentItemIndex;
300
301 // Spec: The history stack may be reset to a state where it only contains the current card.
302 addItem(currentItem);
303 }
304 #endif
305
306 }; // namespace WebCore
307