summaryrefslogtreecommitdiffstats
path: root/chrome/browser/navigation_controller_base.cc
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
commit09911bf300f1a419907a9412154760efd0b7abc3 (patch)
treef131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/navigation_controller_base.cc
parent586acc5fe142f498261f52c66862fa417c3d52d2 (diff)
downloadchromium_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.cc414
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