Home | History | Annotate | Download | only in history
      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