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 39namespace WebCore { 40 41static const unsigned DefaultCapacity = 100; 42static const unsigned NoCurrentItemIndex = UINT_MAX; 43 44BackForwardList::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 53BackForwardList::~BackForwardList() 54{ 55 ASSERT(m_closed); 56} 57 58void 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 90void BackForwardList::goBack() 91{ 92 ASSERT(m_current > 0); 93 if (m_current > 0) { 94 m_current--; 95 } 96} 97 98void 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 106void 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 120HistoryItem* BackForwardList::backItem() 121{ 122 if (m_current && m_current != NoCurrentItemIndex) 123 return m_entries[m_current - 1].get(); 124 return 0; 125} 126 127HistoryItem* BackForwardList::currentItem() 128{ 129 if (m_current != NoCurrentItemIndex) 130 return m_entries[m_current].get(); 131 return 0; 132} 133 134HistoryItem* 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 141void 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 151void 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 167int BackForwardList::capacity() 168{ 169 return m_capacity; 170} 171 172void 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 189bool BackForwardList::enabled() 190{ 191 return m_enabled; 192} 193 194void 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 204int BackForwardList::backListCount() 205{ 206 return m_current == NoCurrentItemIndex ? 0 : m_current; 207} 208 209int BackForwardList::forwardListCount() 210{ 211 return m_current == NoCurrentItemIndex ? 0 : (int)m_entries.size() - (m_current + 1); 212} 213 214HistoryItem* 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 226HistoryItemVector& BackForwardList::entries() 227{ 228 return m_entries; 229} 230 231#if PLATFORM(IOS) 232unsigned BackForwardList::current() 233{ 234 return m_current; 235} 236 237void BackForwardList::setCurrent(unsigned newCurrent) 238{ 239 m_current = newCurrent; 240} 241 242bool 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 257void 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 268bool BackForwardList::closed() 269{ 270 return m_closed; 271} 272 273void 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 295bool BackForwardList::containsItem(HistoryItem* entry) 296{ 297 return m_entryHash.contains(entry); 298} 299 300}; // namespace WebCore 301