Home | History | Annotate | Download | only in chromium
      1 /*
      2  * Copyright (C) 2010 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 #include "TestNavigationController.h"
     33 
     34 #include "TestShell.h"
     35 #include <wtf/Assertions.h>
     36 
     37 using namespace WebKit;
     38 using namespace std;
     39 
     40 // ----------------------------------------------------------------------------
     41 // TestNavigationEntry
     42 
     43 PassRefPtr<TestNavigationEntry> TestNavigationEntry::create()
     44 {
     45     return adoptRef(new TestNavigationEntry);
     46 }
     47 
     48 PassRefPtr<TestNavigationEntry> TestNavigationEntry::create(
     49     int pageID, const WebURL& url, const WebString& title, const WebString& targetFrame)
     50 {
     51     return adoptRef(new TestNavigationEntry(pageID, url, title, targetFrame));
     52 }
     53 
     54 TestNavigationEntry::TestNavigationEntry()
     55     : m_pageID(-1) {}
     56 
     57 TestNavigationEntry::TestNavigationEntry(
     58     int pageID, const WebURL& url, const WebString& title, const WebString& targetFrame)
     59     : m_pageID(pageID)
     60     , m_url(url)
     61     , m_title(title)
     62     , m_targetFrame(targetFrame) {}
     63 
     64 TestNavigationEntry::~TestNavigationEntry() {}
     65 
     66 void TestNavigationEntry::setContentState(const WebHistoryItem& state)
     67 {
     68     m_state = state;
     69 }
     70 
     71 // ----------------------------------------------------------------------------
     72 // TestNavigationController
     73 
     74 TestNavigationController::TestNavigationController(NavigationHost* host)
     75     : m_pendingEntry(0)
     76     , m_lastCommittedEntryIndex(-1)
     77     , m_pendingEntryIndex(-1)
     78     , m_host(host)
     79     , m_maxPageID(-1) {}
     80 
     81 TestNavigationController::~TestNavigationController()
     82 {
     83     discardPendingEntry();
     84 }
     85 
     86 void TestNavigationController::reset()
     87 {
     88     m_entries.clear();
     89     discardPendingEntry();
     90 
     91     m_lastCommittedEntryIndex = -1;
     92 }
     93 
     94 void TestNavigationController::reload()
     95 {
     96     // Base the navigation on where we are now...
     97     int currentIndex = currentEntryIndex();
     98 
     99     // If we are no where, then we can't reload.  TODO(darin): We should add a
    100     // CanReload method.
    101     if (currentIndex == -1)
    102         return;
    103 
    104     discardPendingEntry();
    105 
    106     m_pendingEntryIndex = currentIndex;
    107     navigateToPendingEntry(true);
    108 }
    109 
    110 void TestNavigationController::goToOffset(int offset)
    111 {
    112     int index = m_lastCommittedEntryIndex + offset;
    113     if (index < 0 || index >= entryCount())
    114         return;
    115 
    116     goToIndex(index);
    117 }
    118 
    119 void TestNavigationController::goToIndex(int index)
    120 {
    121     ASSERT(index >= 0);
    122     ASSERT(index < static_cast<int>(m_entries.size()));
    123 
    124     discardPendingEntry();
    125 
    126     m_pendingEntryIndex = index;
    127     navigateToPendingEntry(false);
    128 }
    129 
    130 void TestNavigationController::loadEntry(TestNavigationEntry* entry)
    131 {
    132     // When navigating to a new page, we don't know for sure if we will actually
    133     // end up leaving the current page.  The new page load could for example
    134     // result in a download or a 'no content' response (e.g., a mailto: URL).
    135     discardPendingEntry();
    136     m_pendingEntry = entry;
    137     navigateToPendingEntry(false);
    138 }
    139 
    140 
    141 TestNavigationEntry* TestNavigationController::lastCommittedEntry() const
    142 {
    143     if (m_lastCommittedEntryIndex == -1)
    144         return 0;
    145     return m_entries[m_lastCommittedEntryIndex].get();
    146 }
    147 
    148 TestNavigationEntry* TestNavigationController::activeEntry() const
    149 {
    150     TestNavigationEntry* entry = m_pendingEntry.get();
    151     if (!entry)
    152         entry = lastCommittedEntry();
    153     return entry;
    154 }
    155 
    156 int TestNavigationController::currentEntryIndex() const
    157 {
    158     if (m_pendingEntryIndex != -1)
    159         return m_pendingEntryIndex;
    160     return m_lastCommittedEntryIndex;
    161 }
    162 
    163 
    164 TestNavigationEntry* TestNavigationController::entryAtIndex(int index) const
    165 {
    166     if (index < 0 || index >= entryCount())
    167         return 0;
    168     return m_entries[index].get();
    169 }
    170 
    171 TestNavigationEntry* TestNavigationController::entryWithPageID(int32_t pageID) const
    172 {
    173     int index = entryIndexWithPageID(pageID);
    174     return (index != -1) ? m_entries[index].get() : 0;
    175 }
    176 
    177 void TestNavigationController::didNavigateToEntry(TestNavigationEntry* entry)
    178 {
    179     // If the entry is that of a page with PageID larger than any this Tab has
    180     // seen before, then consider it a new navigation.
    181     if (entry->pageID() > maxPageID()) {
    182         insertEntry(entry);
    183         return;
    184     }
    185 
    186     // Otherwise, we just need to update an existing entry with matching PageID.
    187     // If the existing entry corresponds to the entry which is pending, then we
    188     // must update the current entry index accordingly.  When navigating to the
    189     // same URL, a new PageID is not created.
    190 
    191     int existingEntryIndex = entryIndexWithPageID(entry->pageID());
    192     TestNavigationEntry* existingEntry = (existingEntryIndex != -1) ?
    193         m_entries[existingEntryIndex].get() : 0;
    194     if (!existingEntry) {
    195         // No existing entry, then simply ignore this navigation!
    196     } else if (existingEntry == m_pendingEntry.get()) {
    197         // The given entry might provide a new URL... e.g., navigating back to a
    198         // page in session history could have resulted in a new client redirect.
    199         existingEntry->setURL(entry->URL());
    200         existingEntry->setContentState(entry->contentState());
    201         m_lastCommittedEntryIndex = m_pendingEntryIndex;
    202         m_pendingEntryIndex = -1;
    203         m_pendingEntry.clear();
    204     } else if (m_pendingEntry && m_pendingEntry->pageID() == -1
    205                && GURL(m_pendingEntry->URL()) == GURL(existingEntry->URL().spec())) {
    206         // Not a new navigation
    207         discardPendingEntry();
    208     } else {
    209         // The given entry might provide a new URL... e.g., navigating to a page
    210         // might result in a client redirect, which should override the URL of the
    211         // existing entry.
    212         existingEntry->setURL(entry->URL());
    213         existingEntry->setContentState(entry->contentState());
    214 
    215         // The navigation could have been issued by the renderer, so be sure that
    216         // we update our current index.
    217         m_lastCommittedEntryIndex = existingEntryIndex;
    218     }
    219 
    220     updateMaxPageID();
    221 }
    222 
    223 void TestNavigationController::discardPendingEntry()
    224 {
    225     m_pendingEntry.clear();
    226     m_pendingEntryIndex = -1;
    227 }
    228 
    229 void TestNavigationController::insertEntry(TestNavigationEntry* entry)
    230 {
    231     discardPendingEntry();
    232 
    233     // Prune any entry which are in front of the current entry
    234     int currentSize = static_cast<int>(m_entries.size());
    235     if (currentSize > 0) {
    236         while (m_lastCommittedEntryIndex < (currentSize - 1)) {
    237             m_entries.removeLast();
    238             currentSize--;
    239         }
    240     }
    241 
    242     m_entries.append(RefPtr<TestNavigationEntry>(entry));
    243     m_lastCommittedEntryIndex = static_cast<int>(m_entries.size()) - 1;
    244     updateMaxPageID();
    245 }
    246 
    247 int TestNavigationController::entryIndexWithPageID(int32 pageID) const
    248 {
    249     for (int i = static_cast<int>(m_entries.size()) - 1; i >= 0; --i) {
    250         if (m_entries[i]->pageID() == pageID)
    251             return i;
    252     }
    253     return -1;
    254 }
    255 
    256 void TestNavigationController::navigateToPendingEntry(bool reload)
    257 {
    258     // For session history navigations only the pending_entry_index_ is set.
    259     if (!m_pendingEntry) {
    260         ASSERT(m_pendingEntryIndex != -1);
    261         m_pendingEntry = m_entries[m_pendingEntryIndex];
    262     }
    263 
    264     if (m_host->navigate(*m_pendingEntry.get(), reload)) {
    265         // Note: this is redundant if navigation completed synchronously because
    266         // DidNavigateToEntry call this as well.
    267         updateMaxPageID();
    268     } else
    269         discardPendingEntry();
    270 }
    271 
    272 void TestNavigationController::updateMaxPageID()
    273 {
    274     TestNavigationEntry* entry = activeEntry();
    275     if (entry)
    276         m_maxPageID = max(m_maxPageID, entry->pageID());
    277 }
    278