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