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