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