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