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