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 "HistoryItem.h"
31 #include "Logging.h"
32 #include "PageCache.h"
33 #ifdef ANDROID_HISTORY_CLIENT
34 #include "Frame.h"
35 #include "FrameLoader.h"
36 #include "FrameLoaderClient.h"
37 #include "Page.h"
38 #endif
39
40 using namespace std;
41
42 namespace WebCore {
43
44 static const unsigned DefaultCapacity = 100;
45 static const unsigned NoCurrentItemIndex = UINT_MAX;
46
BackForwardList(Page * page)47 BackForwardList::BackForwardList(Page* page)
48 : m_page(page)
49 , m_current(NoCurrentItemIndex)
50 , m_capacity(DefaultCapacity)
51 , m_closed(true)
52 , m_enabled(true)
53 {
54 }
55
~BackForwardList()56 BackForwardList::~BackForwardList()
57 {
58 ASSERT(m_closed);
59 }
60
addItem(PassRefPtr<HistoryItem> prpItem)61 void BackForwardList::addItem(PassRefPtr<HistoryItem> prpItem)
62 {
63 ASSERT(prpItem);
64 if (m_capacity == 0 || !m_enabled)
65 return;
66
67 // Toss anything in the forward list
68 if (m_current != NoCurrentItemIndex) {
69 unsigned targetSize = m_current + 1;
70 while (m_entries.size() > targetSize) {
71 RefPtr<HistoryItem> item = m_entries.last();
72 m_entries.removeLast();
73 m_entryHash.remove(item);
74 pageCache()->remove(item.get());
75 }
76 }
77
78 // Toss the first item if the list is getting too big, as long as we're not using it
79 // (or even if we are, if we only want 1 entry).
80 if (m_entries.size() == m_capacity && (m_current != 0 || m_capacity == 1)) {
81 RefPtr<HistoryItem> item = m_entries[0];
82 m_entries.remove(0);
83 m_entryHash.remove(item);
84 pageCache()->remove(item.get());
85 m_current--;
86 #ifdef ANDROID_HISTORY_CLIENT
87 m_page->mainFrame()->loader()->client()->dispatchDidRemoveHistoryItem(item.get(), 0);
88 #endif
89 }
90
91 m_entries.append(prpItem);
92 m_entryHash.add(m_entries.last());
93 m_current++;
94 #ifdef ANDROID_HISTORY_CLIENT
95 m_page->mainFrame()->loader()->client()->dispatchDidAddHistoryItem(currentItem());
96 #endif
97 }
98
goBack()99 void BackForwardList::goBack()
100 {
101 ASSERT(m_current > 0);
102 #ifdef ANDROID_HISTORY_CLIENT
103 if (m_current > 0) {
104 m_current--;
105 m_page->mainFrame()->loader()->client()->dispatchDidChangeHistoryIndex(this);
106 }
107 #else
108 if (m_current > 0)
109 m_current--;
110 #endif
111 }
112
goForward()113 void BackForwardList::goForward()
114 {
115 ASSERT(m_current < m_entries.size() - 1);
116 #ifdef ANDROID_HISTORY_CLIENT
117 if (m_current < m_entries.size() - 1) {
118 m_current++;
119 m_page->mainFrame()->loader()->client()->dispatchDidChangeHistoryIndex(this);
120 }
121 #else
122 if (m_current < m_entries.size() - 1)
123 m_current++;
124 #endif
125 }
126
goToItem(HistoryItem * item)127 void BackForwardList::goToItem(HistoryItem* item)
128 {
129 if (!m_entries.size() || !item)
130 return;
131
132 unsigned int index = 0;
133 for (; index < m_entries.size(); ++index)
134 if (m_entries[index] == item)
135 break;
136 #ifdef ANDROID_HISTORY_CLIENT
137 if (index < m_entries.size()) {
138 m_current = index;
139 m_page->mainFrame()->loader()->client()->dispatchDidChangeHistoryIndex(this);
140 }
141 #else
142 if (index < m_entries.size())
143 m_current = index;
144 #endif
145 }
146
backItem()147 HistoryItem* BackForwardList::backItem()
148 {
149 if (m_current && m_current != NoCurrentItemIndex)
150 return m_entries[m_current - 1].get();
151 return 0;
152 }
153
currentItem()154 HistoryItem* BackForwardList::currentItem()
155 {
156 if (m_current != NoCurrentItemIndex)
157 return m_entries[m_current].get();
158 return 0;
159 }
160
forwardItem()161 HistoryItem* BackForwardList::forwardItem()
162 {
163 if (m_entries.size() && m_current < m_entries.size() - 1)
164 return m_entries[m_current + 1].get();
165 return 0;
166 }
167
backListWithLimit(int limit,HistoryItemVector & list)168 void BackForwardList::backListWithLimit(int limit, HistoryItemVector& list)
169 {
170 list.clear();
171 if (m_current != NoCurrentItemIndex) {
172 unsigned first = max((int)m_current - limit, 0);
173 for (; first < m_current; ++first)
174 list.append(m_entries[first]);
175 }
176 }
177
forwardListWithLimit(int limit,HistoryItemVector & list)178 void BackForwardList::forwardListWithLimit(int limit, HistoryItemVector& list)
179 {
180 ASSERT(limit > -1);
181 list.clear();
182 if (!m_entries.size())
183 return;
184
185 unsigned lastEntry = m_entries.size() - 1;
186 if (m_current < lastEntry) {
187 int last = min(m_current + limit, lastEntry);
188 limit = m_current + 1;
189 for (; limit <= last; ++limit)
190 list.append(m_entries[limit]);
191 }
192 }
193
capacity()194 int BackForwardList::capacity()
195 {
196 return m_capacity;
197 }
198
setCapacity(int size)199 void BackForwardList::setCapacity(int size)
200 {
201 while (size < (int)m_entries.size()) {
202 RefPtr<HistoryItem> item = m_entries.last();
203 m_entries.removeLast();
204 m_entryHash.remove(item);
205 pageCache()->remove(item.get());
206 }
207
208 if (!size)
209 m_current = NoCurrentItemIndex;
210 #ifdef ANDROID_HISTORY_CLIENT
211 else if (m_current > m_entries.size() - 1) {
212 m_current = m_entries.size() - 1;
213 m_page->mainFrame()->loader()->client()->dispatchDidChangeHistoryIndex(this);
214 }
215 #else
216 else if (m_current > m_entries.size() - 1)
217 m_current = m_entries.size() - 1;
218 #endif
219
220 m_capacity = size;
221 }
222
enabled()223 bool BackForwardList::enabled()
224 {
225 return m_enabled;
226 }
227
setEnabled(bool enabled)228 void BackForwardList::setEnabled(bool enabled)
229 {
230 m_enabled = enabled;
231 if (!enabled) {
232 int capacity = m_capacity;
233 setCapacity(0);
234 setCapacity(capacity);
235 }
236 }
237
backListCount()238 int BackForwardList::backListCount()
239 {
240 return m_current == NoCurrentItemIndex ? 0 : m_current;
241 }
242
forwardListCount()243 int BackForwardList::forwardListCount()
244 {
245 return m_current == NoCurrentItemIndex ? 0 : (int)m_entries.size() - (m_current + 1);
246 }
247
itemAtIndex(int index)248 HistoryItem* BackForwardList::itemAtIndex(int index)
249 {
250 // Do range checks without doing math on index to avoid overflow.
251 if (index < -(int)m_current)
252 return 0;
253
254 if (index > forwardListCount())
255 return 0;
256
257 return m_entries[index + m_current].get();
258 }
259
entries()260 HistoryItemVector& BackForwardList::entries()
261 {
262 return m_entries;
263 }
264
close()265 void BackForwardList::close()
266 {
267 int size = m_entries.size();
268 for (int i = 0; i < size; ++i)
269 pageCache()->remove(m_entries[i].get());
270 m_entries.clear();
271 m_entryHash.clear();
272 m_page = 0;
273 m_closed = true;
274 }
275
closed()276 bool BackForwardList::closed()
277 {
278 return m_closed;
279 }
280
removeItem(HistoryItem * item)281 void BackForwardList::removeItem(HistoryItem* item)
282 {
283 if (!item)
284 return;
285
286 for (unsigned i = 0; i < m_entries.size(); ++i)
287 if (m_entries[i] == item) {
288 m_entries.remove(i);
289 m_entryHash.remove(item);
290 if (m_current == NoCurrentItemIndex || m_current < i)
291 break;
292 if (m_current > i)
293 m_current--;
294 else {
295 size_t count = m_entries.size();
296 if (m_current >= count)
297 m_current = count ? count-1 : NoCurrentItemIndex;
298 }
299 break;
300 }
301 }
302
containsItem(HistoryItem * entry)303 bool BackForwardList::containsItem(HistoryItem* entry)
304 {
305 return m_entryHash.contains(entry);
306 }
307
308 #if ENABLE(WML)
clearWMLPageHistory()309 void BackForwardList::clearWMLPageHistory()
310 {
311 RefPtr<HistoryItem> currentItem = this->currentItem();
312
313 int size = m_entries.size();
314 for (int i = 0; i < size; ++i)
315 pageCache()->remove(m_entries[i].get());
316
317 m_entries.clear();
318 m_entryHash.clear();
319 m_current = NoCurrentItemIndex;
320
321 // Spec: The history stack may be reset to a state where it only contains the current card.
322 addItem(currentItem);
323 }
324 #endif
325
326 }; // namespace WebCore
327