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