diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
commit | 09911bf300f1a419907a9412154760efd0b7abc3 (patch) | |
tree | f131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/navigation_controller_base.cc | |
parent | 586acc5fe142f498261f52c66862fa417c3d52d2 (diff) | |
download | chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2 |
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/navigation_controller_base.cc')
-rw-r--r-- | chrome/browser/navigation_controller_base.cc | 414 |
1 files changed, 414 insertions, 0 deletions
diff --git a/chrome/browser/navigation_controller_base.cc b/chrome/browser/navigation_controller_base.cc new file mode 100644 index 0000000..b4b3e99 --- /dev/null +++ b/chrome/browser/navigation_controller_base.cc @@ -0,0 +1,414 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/browser/navigation_controller_base.h" + +#include <algorithm> + +#include "base/logging.h" +#include "chrome/browser/navigation_entry.h" +#include "net/base/registry_controlled_domain.h" +#include "webkit/glue/webkit_glue.h" + +// The maximum number of entries that a navigation controller can store. +const static size_t kMaxEntryCount = 50; + +NavigationControllerBase::NavigationControllerBase() + : pending_entry_(NULL), + last_committed_entry_index_(-1), + pending_entry_index_(-1), + max_entry_count_(kMaxEntryCount) { +} + +NavigationControllerBase::~NavigationControllerBase() { + // NOTE: This does NOT invoke Reset as Reset is virtual. + ResetInternal(); +} + +void NavigationControllerBase::Reset() { + ResetInternal(); + + last_committed_entry_index_ = -1; +} + +NavigationEntry* NavigationControllerBase::GetActiveEntry() const { + NavigationEntry* entry = pending_entry_; + if (!entry) + entry = GetLastCommittedEntry(); + return entry; +} + +int NavigationControllerBase::GetCurrentEntryIndex() const { + if (pending_entry_index_ != -1) + return pending_entry_index_; + return last_committed_entry_index_; +} + +NavigationEntry* NavigationControllerBase::GetLastCommittedEntry() const { + if (last_committed_entry_index_ == -1) + return NULL; + return entries_[last_committed_entry_index_]; +} + +int NavigationControllerBase::GetEntryIndexWithPageID( + TabContentsType type, SiteInstance* instance, int32 page_id) const { + // The instance should only be specified for contents displaying web pages. + // TODO(evanm): checking against NEW_TAB_UI and HTML_DLG here is lame. + // It'd be nice for DomUIHost to just use SiteInstances for keeping content + // separated properly. + if (type != TAB_CONTENTS_WEB && + type != TAB_CONTENTS_NEW_TAB_UI && + type != TAB_CONTENTS_ABOUT_UI && + type != TAB_CONTENTS_HTML_DIALOG && + type != TAB_CONTENTS_VIEW_SOURCE) + DCHECK(instance == NULL); + + for (int i = static_cast<int>(entries_.size()) - 1; i >= 0; --i) { + if ((entries_[i]->GetType() == type) && + (entries_[i]->site_instance() == instance) && + (entries_[i]->GetPageID() == page_id)) + return i; + } + return -1; +} + +NavigationEntry* NavigationControllerBase::GetEntryWithPageID( + TabContentsType type, SiteInstance* instance, int32 page_id) const { + int index = GetEntryIndexWithPageID(type, instance, page_id); + return (index != -1) ? entries_[index] : NULL; +} + +NavigationEntry* NavigationControllerBase::GetEntryAtOffset(int offset) const { + int index = last_committed_entry_index_ + offset; + if (index < 0 || index >= GetEntryCount()) + return NULL; + + return entries_[index]; +} + +bool NavigationControllerBase::CanStop() const { + // TODO(darin): do we have something pending that we can stop? + return false; +} + +bool NavigationControllerBase::CanGoBack() const { + return entries_.size() > 1 && GetCurrentEntryIndex() > 0; +} + +bool NavigationControllerBase::CanGoForward() const { + int index = GetCurrentEntryIndex(); + return index >= 0 && index < (static_cast<int>(entries_.size()) - 1); +} + +void NavigationControllerBase::GoBack() { + if (!CanGoBack()) { + NOTREACHED(); + return; + } + + // Base the navigation on where we are now... + int current_index = GetCurrentEntryIndex(); + + DiscardPendingEntry(); + + pending_entry_index_ = current_index - 1; + NavigateToPendingEntry(false); +} + +void NavigationControllerBase::GoForward() { + if (!CanGoForward()) { + NOTREACHED(); + return; + } + + // Base the navigation on where we are now... + int current_index = GetCurrentEntryIndex(); + + DiscardPendingEntry(); + + pending_entry_index_ = current_index + 1; + NavigateToPendingEntry(false); +} + +void NavigationControllerBase::GoToIndex(int index) { + if (index < 0 || index >= static_cast<int>(entries_.size())) { + NOTREACHED(); + return; + } + + DiscardPendingEntry(); + + pending_entry_index_ = index; + NavigateToPendingEntry(false); +} + +void NavigationControllerBase::GoToOffset(int offset) { + int index = last_committed_entry_index_ + offset; + if (index < 0 || index >= GetEntryCount()) + return; + + GoToIndex(index); +} + +void NavigationControllerBase::Stop() { + DCHECK(CanStop()); + + // TODO(darin): we probably want to just call Stop on the active tab + // contents, but should we also call DiscardPendingEntry? + NOTREACHED() << "implement me"; +} + +void NavigationControllerBase::Reload() { + // Base the navigation on where we are now... + int current_index = GetCurrentEntryIndex(); + + // If we are no where, then we can't reload. TODO(darin): We should add a + // CanReload method. + if (current_index == -1) + return; + + // TODO(pkasting): http://b/1113085 Should this use DiscardPendingEntry()? + DiscardPendingEntryInternal(); + + pending_entry_index_ = current_index; + entries_[pending_entry_index_]->SetTransitionType(PageTransition::RELOAD); + NavigateToPendingEntry(true); +} + +void NavigationControllerBase::LoadEntry(NavigationEntry* entry) { + // When navigating to a new page, we don't know for sure if we will actually + // end up leaving the current page. The new page load could for example + // result in a download or a 'no content' response (e.g., a mailto: URL). + + // TODO(pkasting): http://b/1113085 Should this use DiscardPendingEntry()? + DiscardPendingEntryInternal(); + pending_entry_ = entry; + NavigateToPendingEntry(false); +} + +/* static */ +void NavigationControllerBase::SetContentStateIfEmpty( + NavigationEntry* entry) { + if (entry->GetContentState().empty() && + (entry->GetType() == TAB_CONTENTS_WEB || + entry->GetType() == TAB_CONTENTS_NEW_TAB_UI || + entry->GetType() == TAB_CONTENTS_ABOUT_UI || + entry->GetType() == TAB_CONTENTS_HTML_DIALOG)) { + // The state is empty and the url will be rendered by WebKit. An empty + // state is treated as a new navigation by WebKit, which would mean + // losing the navigation entries and generating a new navigation + // entry after this one. We don't want that. To avoid this we create + // a valid state which WebKit will not treat as a new navigation. + entry->SetContentState( + webkit_glue::CreateHistoryStateForURL(entry->GetURL())); + } +} + +void NavigationControllerBase::DidNavigateToEntry(NavigationEntry* entry) { + SetContentStateIfEmpty(entry); + + entry->set_restored(false); + + // If the entry is that of a page with PageID larger than any this Tab has + // seen before, then consider it a new navigation. Note that if the entry + // has a SiteInstance, it should be the same as the SiteInstance of the + // active WebContents, because we have just navigated to it. + if (entry->GetPageID() > GetMaxPageID()) { + InsertEntry(entry); + return; + } + + // Otherwise, we just need to update an existing entry with matching PageID. + // If the existing entry corresponds to the entry which is pending, then we + // must update the current entry index accordingly. When navigating to the + // same URL, a new PageID is not created. + + int existing_entry_index = GetEntryIndexWithPageID(entry->GetType(), + entry->site_instance(), + entry->GetPageID()); + NavigationEntry* existing_entry = + (existing_entry_index != -1) ? entries_[existing_entry_index] : NULL; + if (!existing_entry) { + // No existing entry, then simply ignore this navigation! + DLOG(WARNING) << "ignoring navigation for page: " << entry->GetPageID(); + } else if ((existing_entry != pending_entry_) && pending_entry_ && + (pending_entry_->GetPageID() == -1) && + (pending_entry_->GetURL() == existing_entry->GetURL())) { + // Not a new navigation. + existing_entry->set_unique_id(pending_entry_->unique_id()); + DiscardPendingEntry(); + } else { + DCHECK(existing_entry != entry); + // The given entry might provide a new URL... e.g., navigating back to a + // page in session history could have resulted in a new client redirect. + // The given entry might also provide a new title (typically an empty title + // to overwrite the existing title). + existing_entry->SetURL(entry->GetURL()); + existing_entry->SetTitle(entry->GetTitle()); + existing_entry->SetFavIconURL(entry->GetFavIconURL()); + existing_entry->SetFavIcon(entry->GetFavIcon()); + existing_entry->SetValidFavIcon(entry->IsValidFavIcon()); + existing_entry->SetSecurityStyle(entry->GetSecurityStyle()); + existing_entry->SetContentState(entry->GetContentState()); + + const int prev_entry_index = last_committed_entry_index_; + if (existing_entry == pending_entry_) { + DCHECK(pending_entry_index_ != -1); + last_committed_entry_index_ = pending_entry_index_; + // TODO(pkasting): http://b/1113085 Should this use DiscardPendingEntry()? + DiscardPendingEntryInternal(); + } else { + // NOTE: Do not update the unique ID here, as we don't want infobars etc. + // to dismiss. + + // The navigation could have been issued by the renderer, so be sure that + // we update our current index. + last_committed_entry_index_ = existing_entry_index; + } + IndexOfActiveEntryChanged(prev_entry_index); + } + + delete entry; + + NotifyNavigationStateChanged(); +} + +void NavigationControllerBase::DiscardPendingEntry() { + DiscardPendingEntryInternal(); + + // Derived classes may do additional things in this case. +} + +int NavigationControllerBase::GetIndexOfEntry( + const NavigationEntry* entry) const { + const NavigationEntries::const_iterator i(std::find(entries_.begin(), + entries_.end(), entry)); + return (i == entries_.end()) ? -1 : static_cast<int>(i - entries_.begin()); +} + +void NavigationControllerBase::DiscardPendingEntryInternal() { + if (pending_entry_index_ == -1) + delete pending_entry_; + pending_entry_ = NULL; + pending_entry_index_ = -1; +} + +void NavigationControllerBase::InsertEntry(NavigationEntry* entry) { + DCHECK(entry->GetTransitionType() != PageTransition::AUTO_SUBFRAME); + + // Copy the pending entry's unique ID to the committed entry. + // I don't know if pending_entry_index_ can be other than -1 here. + const NavigationEntry* const pending_entry = (pending_entry_index_ == -1) ? + pending_entry_ : entries_[pending_entry_index_]; + if (pending_entry) + entry->set_unique_id(pending_entry->unique_id()); + + DiscardPendingEntryInternal(); + + int current_size = static_cast<int>(entries_.size()); + + // Prune any entries which are in front of the current entry. + if (current_size > 0) { + while (last_committed_entry_index_ < (current_size - 1)) { + delete entries_[current_size - 1]; + entries_.pop_back(); + current_size--; + } + NotifyPrunedEntries(); + } + + if (entries_.size() >= max_entry_count_) + RemoveEntryAtIndex(0); + + entries_.push_back(entry); + last_committed_entry_index_ = static_cast<int>(entries_.size()) - 1; + + NotifyNavigationStateChanged(); +} + +void NavigationControllerBase::RemoveLastEntry() { + int current_size = static_cast<int>(entries_.size()); + + if (current_size > 0) { + if (pending_entry_ == entries_[current_size - 1] || + pending_entry_index_ == current_size - 1) + DiscardPendingEntryInternal(); + + delete entries_[current_size - 1]; + entries_.pop_back(); + + if (last_committed_entry_index_ >= current_size - 1) + last_committed_entry_index_ = current_size - 2; + + NotifyPrunedEntries(); + } + NotifyNavigationStateChanged(); +} + +void NavigationControllerBase::RemoveEntryAtIndex(int index) { + if (index >= static_cast<int>(entries_.size()) || + index == pending_entry_index_ || index == last_committed_entry_index_) { + NOTREACHED(); + return; + } + + delete entries_[index]; + entries_.erase(entries_.begin() + index); + + if (last_committed_entry_index_ >= index) { + if (!entries_.empty()) + last_committed_entry_index_--; + else + last_committed_entry_index_ = -1; + } + + NotifyPrunedEntries(); + NotifyNavigationStateChanged(); +} + +void NavigationControllerBase::ResetInternal() { + // WARNING: this is invoked from the destructor, be sure not to invoke any + // virtual methods from this. + for (int i = 0, c = static_cast<int>(entries_.size()); i < c; ++i) + delete entries_[i]; + entries_.clear(); + + DiscardPendingEntryInternal(); +} + +#ifndef NDEBUG + +void NavigationControllerBase::Dump() { + int i, c; + for (i = 1, c = static_cast<int>(entries_.size()); i < c; ++i) { + DLOG(INFO) << entries_[i]->GetURL().spec(); + } +} + +#endif |