summaryrefslogtreecommitdiffstats
path: root/chrome/browser/navigation_controller.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.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.cc')
-rw-r--r--chrome/browser/navigation_controller.cc712
1 files changed, 712 insertions, 0 deletions
diff --git a/chrome/browser/navigation_controller.cc b/chrome/browser/navigation_controller.cc
new file mode 100644
index 0000000..a31db5f
--- /dev/null
+++ b/chrome/browser/navigation_controller.cc
@@ -0,0 +1,712 @@
+// 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.h"
+
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "chrome/common/navigation_types.h"
+#include "chrome/common/resource_bundle.h"
+#include "chrome/common/scoped_vector.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/dom_ui/dom_ui_host.h"
+#include "chrome/browser/navigation_entry.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/repost_form_warning_dialog.h"
+#include "chrome/browser/site_instance.h"
+#include "chrome/browser/tab_contents.h"
+#include "chrome/browser/tab_contents_delegate.h"
+#include "chrome/common/chrome_switches.h"
+#include "net/base/net_util.h"
+
+// TabContentsCollector ---------------------------------------------------
+
+// We never destroy a TabContents synchronously because there are some
+// complex code path that cause the current TabContents to be in the call
+// stack. So instead, we use a TabContentsCollector which either destroys
+// the TabContents or does nothing if it has been cancelled.
+class TabContentsCollector : public Task {
+ public:
+ TabContentsCollector(NavigationController* target,
+ TabContentsType target_type)
+ : target_(target),
+ target_type_(target_type) {
+ }
+
+ void Cancel() {
+ target_ = NULL;
+ }
+
+ virtual void Run() {
+ if (target_) {
+ // Note: this will cancel this task as a side effect so target_ is
+ // now null.
+ TabContents* tc = target_->GetTabContents(target_type_);
+ tc->Destroy();
+ }
+ }
+
+ private:
+ // The NavigationController we are acting on.
+ NavigationController* target_;
+
+ // The TabContentsType that needs to be collected.
+ TabContentsType target_type_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(TabContentsCollector);
+};
+
+// NavigationController ---------------------------------------------------
+
+// static
+bool NavigationController::check_for_repost_ = true;
+
+// Creates a new NavigationEntry for each TabNavigation in navigations, adding
+// the NavigationEntry to entries. This is used during session restore.
+static void CreateNavigationEntriesFromTabNavigations(
+ const std::vector<TabNavigation>& navigations,
+ std::vector<NavigationEntry*>* entries) {
+ // Create a NavigationEntry for each of the navigations.
+ for (std::vector<TabNavigation>::const_iterator i =
+ navigations.begin(); i != navigations.end(); ++i) {
+ const TabNavigation& navigation = *i;
+
+ GURL real_url = navigation.url;
+ TabContentsType type = TabContents::TypeForURL(&real_url);
+ DCHECK(type != TAB_CONTENTS_UNKNOWN_TYPE);
+
+ NavigationEntry* entry = new NavigationEntry(
+ type,
+ NULL, // The site instance for restored tabs is sent on naviagion
+ // (WebContents::GetSiteInstanceForEntry).
+ static_cast<int>(i - navigations.begin()),
+ real_url,
+ navigation.title,
+ // Use a transition type of reload so that we don't incorrectly
+ // increase the typed count.
+ PageTransition::RELOAD);
+ entry->SetDisplayURL(navigation.url);
+ entry->SetContentState(navigation.state);
+ entry->SetHasPostData(navigation.type_mask & TabNavigation::HAS_POST_DATA);
+ entries->push_back(entry);
+ }
+}
+
+// Configure all the NavigationEntries in entries for restore. This resets
+// the transition type to reload and makes sure the content state isn't empty.
+static void ConfigureEntriesForRestore(
+ std::vector<NavigationEntry*>* entries) {
+ for (size_t i = 0, count = entries->size(); i < count; ++i) {
+ // Use a transition type of reload so that we don't incorrectly increase
+ // the typed count.
+ (*entries)[i]->SetTransitionType(PageTransition::RELOAD);
+ (*entries)[i]->set_restored(true);
+ // NOTE(darin): This code is only needed for backwards compat.
+ NavigationControllerBase::SetContentStateIfEmpty((*entries)[i]);
+ }
+}
+
+NavigationController::NavigationController(TabContents* contents,
+ Profile* profile)
+ : profile_(profile),
+ active_contents_(contents),
+ alternate_nav_url_fetcher_entry_unique_id_(0),
+ max_restored_page_id_(-1),
+ ssl_manager_(this, NULL),
+ needs_reload_(false),
+ load_pending_entry_when_active_(false) {
+ if (contents)
+ RegisterTabContents(contents);
+ DCHECK(profile_);
+ profile_->RegisterNavigationController(this);
+}
+
+NavigationController::NavigationController(
+ Profile* profile,
+ const std::vector<TabNavigation>& navigations,
+ int selected_navigation,
+ HWND parent)
+ : profile_(profile),
+ active_contents_(NULL),
+ alternate_nav_url_fetcher_entry_unique_id_(0),
+ max_restored_page_id_(-1),
+ ssl_manager_(this, NULL),
+ needs_reload_(true),
+ load_pending_entry_when_active_(false) {
+ DCHECK(profile_);
+ DCHECK(selected_navigation >= 0 &&
+ selected_navigation < static_cast<int>(navigations.size()));
+
+ profile_->RegisterNavigationController(this);
+
+ // Populate entries_ from the supplied TabNavigations.
+ CreateNavigationEntriesFromTabNavigations(navigations, &entries_);
+
+ // And finish the restore.
+ FinishRestore(parent, selected_navigation);
+}
+
+NavigationController::~NavigationController() {
+ DCHECK(tab_contents_map_.empty());
+ DCHECK(tab_contents_collector_map_.empty());
+
+ SessionService* session_service = GetSessionService();
+ if (session_service)
+ session_service->TabClosed(window_id_, session_id());
+
+ profile_->UnregisterNavigationController(this);
+
+ NotificationService::current()->Notify(
+ NOTIFY_TAB_CLOSED,
+ Source<NavigationController>(this),
+ NotificationService::NoDetails());
+}
+
+TabContents* NavigationController::GetTabContents(TabContentsType t) {
+ // Make sure the TabContents is no longer scheduled for collection.
+ CancelTabContentsCollection(t);
+ return tab_contents_map_[t];
+}
+
+void NavigationController::Reset() {
+ NavigationControllerBase::Reset();
+
+ NotifyPrunedEntries();
+}
+
+void NavigationController::Reload() {
+ // TODO(pkasting): http://b/1113085 Should this use DiscardPendingEntry()?
+ DiscardPendingEntryInternal();
+ int current_index = GetCurrentEntryIndex();
+ if (check_for_repost_ && current_index != -1 &&
+ GetEntryAtIndex(current_index)->HasPostData() &&
+ active_contents_->AsWebContents() &&
+ !active_contents_->AsWebContents()->showing_repost_interstitial()) {
+ // The user is asking to reload a page with POST data and we're not showing
+ // the POST interstitial. Prompt to make sure they really want to do this.
+ // If they do, RepostFormWarningDialog calls us back with
+ // ReloadDontCheckForRepost.
+ active_contents_->Activate();
+ RepostFormWarningDialog::RunRepostFormWarningDialog(this);
+ } else {
+ NavigationControllerBase::Reload();
+ }
+}
+
+void NavigationController::ReloadDontCheckForRepost() {
+ NavigationControllerBase::Reload();
+}
+
+void NavigationController::Destroy() {
+ // First, clean out all NULL entries in the map so that we know empty map
+ // means all tabs destroyed. This is needed since TabContentsWasDestroyed()
+ // won't get called for types that are in our map with a NULL contents.
+ for (int t = 0; t < TAB_CONTENTS_NUM_TYPES; t++) {
+ TabContentsMap::iterator i =
+ tab_contents_map_.find(static_cast<TabContentsType>(t));
+ if (i != tab_contents_map_.end() && !i->second)
+ tab_contents_map_.erase(i);
+ }
+
+ // Now close all tab contents owned by this controller. We make a list on
+ // the stack because they are removed from the map as they are Destroyed
+ // (invalidating the iterators), which may or may not occur synchronously.
+ std::list<TabContents*> tabs_to_destroy;
+ for (TabContentsMap::iterator i = tab_contents_map_.begin();
+ i != tab_contents_map_.end(); ++i) {
+ DCHECK(i->second);
+ tabs_to_destroy.push_back(i->second);
+ }
+
+ // Cancel all the TabContentsCollectors.
+ for (TabContentsCollectorMap::iterator i =
+ tab_contents_collector_map_.begin();
+ i != tab_contents_collector_map_.end(); ++i) {
+ DCHECK(i->second);
+ i->second->Cancel();
+ }
+
+ // Finally destroy all the tab contents.
+ for (std::list<TabContents*>::iterator i = tabs_to_destroy.begin();
+ i != tabs_to_destroy.end(); ++i) {
+ (*i)->Destroy();
+ }
+ // We are deleted at this point.
+}
+
+void NavigationController::TabContentsWasDestroyed(TabContentsType type) {
+ TabContentsMap::iterator i = tab_contents_map_.find(type);
+ DCHECK(i != tab_contents_map_.end());
+ tab_contents_map_.erase(i);
+
+ // Make sure we cancel any collector for that TabContents.
+ TabContentsCollectorMap::iterator ci = tab_contents_collector_map_.find(type);
+ if (ci != tab_contents_collector_map_.end()) {
+ DCHECK(ci->second);
+ ci->second->Cancel();
+ }
+
+ // If that was the last tab to be destroyed, delete ourselves.
+ if (tab_contents_map_.empty())
+ delete this;
+}
+
+NavigationEntry* NavigationController::CreateNavigationEntry(
+ const GURL& url, PageTransition::Type transition) {
+ GURL real_url = url;
+ TabContentsType type;
+
+ // If the active contents supports |url|, use it.
+ // Note: in both cases, we give TabContents a chance to rewrite the URL.
+ TabContents* active = active_contents();
+ if (active && active->SupportsURL(&real_url))
+ type = active->type();
+ else
+ type = TabContents::TypeForURL(&real_url);
+
+ NavigationEntry* entry = new NavigationEntry(type, NULL, -1, real_url,
+ std::wstring(), transition);
+ entry->SetDisplayURL(url);
+ entry->SetUserTypedURL(url);
+ if (url.SchemeIsFile()) {
+ entry->SetTitle(file_util::GetFilenameFromPath(UTF8ToWide(url.host() +
+ url.path())));
+ }
+ return entry;
+}
+
+void NavigationController::LoadURL(const GURL& url,
+ PageTransition::Type transition) {
+ // The user initiated a load, we don't need to reload anymore.
+ needs_reload_ = false;
+
+ NavigationEntry* entry = CreateNavigationEntry(url, transition);
+
+ LoadEntry(entry);
+}
+
+void NavigationController::LoadURLLazily(const GURL& url,
+ PageTransition::Type type,
+ const std::wstring& title,
+ SkBitmap* icon) {
+ NavigationEntry* entry = CreateNavigationEntry(url, type);
+ entry->SetTitle(title);
+ if (icon)
+ entry->SetFavIcon(*icon);
+
+ // TODO(pkasting): http://b/1113085 Should this use DiscardPendingEntry()?
+ DiscardPendingEntryInternal();
+ pending_entry_ = entry;
+ load_pending_entry_when_active_ = true;
+}
+
+bool NavigationController::LoadingURLLazily() {
+ return load_pending_entry_when_active_;
+}
+
+const std::wstring& NavigationController::GetLazyTitle() const {
+ if (pending_entry_)
+ return pending_entry_->GetTitle();
+ else
+ return EmptyWString();
+}
+
+const SkBitmap& NavigationController::GetLazyFavIcon() const {
+ if (pending_entry_) {
+ return pending_entry_->GetFavIcon();
+ } else {
+ ResourceBundle &rb = ResourceBundle::GetSharedInstance();
+ return *rb.GetBitmapNamed(IDR_DEFAULT_FAVICON);
+ }
+}
+
+void NavigationController::EntryUpdated(NavigationEntry* entry) {
+ if (entry == GetActiveEntry()) {
+ // Someone has modified our active navigation entry.
+ NotifyNavigationStateChanged();
+ }
+}
+
+void NavigationController::SetAlternateNavURLFetcher(
+ AlternateNavURLFetcher* alternate_nav_url_fetcher) {
+ DCHECK(!alternate_nav_url_fetcher_.get());
+ DCHECK(pending_entry_);
+ alternate_nav_url_fetcher_.reset(alternate_nav_url_fetcher);
+ alternate_nav_url_fetcher_entry_unique_id_ = pending_entry_->unique_id();
+}
+
+void NavigationController::DidNavigateToEntry(NavigationEntry* entry) {
+ DCHECK(active_contents_);
+ DCHECK(entry->GetType() == active_contents_->type());
+
+ NavigationControllerBase::DidNavigateToEntry(entry);
+ // entry is now deleted
+
+ if (alternate_nav_url_fetcher_.get()) {
+ // Because this call may synchronously show an infobar, we do it last, to
+ // make sure all other state is stable and the infobar won't get blown away
+ // by some transition.
+ alternate_nav_url_fetcher_->OnNavigatedToEntry();
+ }
+
+ // If the previous or next entry uses a different tab contents it is now a
+ // safe time to schedule collection because a navigation is neccessary to
+ // get back to them.
+ int index = GetCurrentEntryIndex();
+ if (index < 0 || GetPendingEntryIndex() != -1)
+ return;
+
+ TabContentsType active_type = GetEntryAtIndex(index)->GetType();
+ if (index > 0) {
+ NavigationEntry* ne = GetEntryAtIndex(index - 1);
+ if (ne->GetType() != active_type)
+ ScheduleTabContentsCollection(ne->GetType());
+ }
+ if (index < (GetEntryCount() - 1)) {
+ NavigationEntry* ne = GetEntryAtIndex(index + 1);
+ if (ne->GetType() != active_type)
+ ScheduleTabContentsCollection(ne->GetType());
+ }
+}
+
+void NavigationController::DiscardPendingEntry() {
+ NavigationControllerBase::DiscardPendingEntry();
+
+ // Synchronize the active_contents_ to the last committed entry.
+ NavigationEntry* last_entry = GetLastCommittedEntry();
+ if (last_entry && last_entry->GetType() != active_contents_->type()) {
+ TabContents* from_contents = active_contents_;
+ from_contents->SetActive(false);
+
+ // Switch back to the previous tab contents.
+ active_contents_ = GetTabContents(last_entry->GetType());
+ DCHECK(active_contents_);
+
+ active_contents_->SetActive(true);
+
+ // If we are transitioning from two types of WebContents, we need to migrate
+ // the download shelf if it is visible. The download shelf may have been
+ // created before the error that caused us to discard the entry.
+ WebContents::MigrateShelfView(from_contents, active_contents_);
+
+ if (from_contents->delegate()) {
+ from_contents->delegate()->ReplaceContents(from_contents,
+ active_contents_);
+ }
+
+ // The entry we just discarded needed a different TabContents type. We no
+ // longer need it but we can't destroy it just yet because the TabContents
+ // is very likely involved in the current stack.
+ DCHECK(from_contents != active_contents_);
+ ScheduleTabContentsCollection(from_contents->type());
+ }
+
+ // Note: this may be redundant in some cases. we may want to optimize away
+ // the redundant notifications.
+ NotifyNavigationStateChanged();
+}
+
+void NavigationController::InsertEntry(NavigationEntry* entry) {
+ DCHECK(entry);
+
+ NavigationControllerBase::InsertEntry(entry);
+
+ int index = GetIndexOfEntry(entry);
+ DCHECK(entries_.size() > 0 && index != -1);
+ SyncSessionWithSelectedIndex(index);
+
+ active_contents_->NotifyDidNavigate(NAVIGATION_NEW, 0);
+}
+
+void NavigationController::SetWindowID(const SessionID& id) {
+ window_id_ = id;
+
+ SessionService* session_service = GetSessionService();
+ if (session_service)
+ session_service->SetTabWindow(window_id_, session_id());
+}
+
+void NavigationController::NavigateToPendingEntry(bool reload) {
+ TabContents* from_contents = active_contents_;
+
+ // For session history navigations only the pending_entry_index_ is set.
+ if (!pending_entry_) {
+ DCHECK(pending_entry_index_ != -1);
+ pending_entry_ = entries_[pending_entry_index_];
+ }
+
+ // Reset the security states as any SSL error may have been resolved since we
+ // last visited that page.
+ pending_entry_->ResetSSLStates();
+
+ if (from_contents && from_contents->type() != pending_entry_->GetType())
+ from_contents->SetActive(false);
+
+ HWND parent =
+ from_contents ? GetParent(from_contents->GetContainerHWND()) : 0;
+ TabContents* contents =
+ GetTabContentsCreateIfNecessary(parent, *pending_entry_);
+
+ contents->SetActive(true);
+ active_contents_ = contents;
+
+ if (from_contents && from_contents != contents) {
+ if (from_contents->delegate())
+ from_contents->delegate()->ReplaceContents(from_contents, contents);
+ }
+
+ if (contents->Navigate(*pending_entry_, reload)) {
+ // Note: this is redundant if navigation completed synchronously because
+ // DidNavigateToEntry call this as well.
+ NotifyNavigationStateChanged();
+ } else {
+ DiscardPendingEntry();
+ }
+}
+
+void NavigationController::NotifyNavigationStateChanged() {
+ // Reset the Alternate Nav URL Fetcher if we're loading some page it doesn't
+ // care about. We must do this before calling Notify() below as that may
+ // result in the creation of a new fetcher.
+ const NavigationEntry* const entry = GetActiveEntry();
+ if (!entry ||
+ (entry->unique_id() != alternate_nav_url_fetcher_entry_unique_id_)) {
+ alternate_nav_url_fetcher_.reset();
+ alternate_nav_url_fetcher_entry_unique_id_ = 0;
+ }
+
+ // TODO(pkasting): http://b/1113079 Probably these explicit notification paths
+ // should be removed, and interested parties should just listen for the
+ // notification below instead.
+ ssl_manager_.NavigationStateChanged();
+ active_contents_->NotifyNavigationStateChanged(
+ TabContents::INVALIDATE_EVERYTHING);
+
+ NotificationService::current()->Notify(
+ NOTIFY_NAVIGATION_STATE_CHANGED,
+ Source<NavigationController>(this),
+ NotificationService::NoDetails());
+}
+
+void NavigationController::NotifyPrunedEntries() {
+ SessionService* session_service = GetSessionService();
+ if (!session_service)
+ return;
+ session_service->TabNavigationPathPruned(window_id(), session_id(),
+ GetEntryCount());
+}
+
+void NavigationController::IndexOfActiveEntryChanged(
+ int prev_committed_index) {
+ NavigationType nav_type = NAVIGATION_BACK_FORWARD;
+ int relative_navigation_offset =
+ GetLastCommittedEntryIndex() - prev_committed_index;
+ if (relative_navigation_offset == 0) {
+ nav_type = NAVIGATION_REPLACE;
+ }
+ active_contents_->NotifyDidNavigate(nav_type, relative_navigation_offset);
+ if (GetCurrentEntryIndex() != -1)
+ SyncSessionWithSelectedIndex(GetCurrentEntryIndex());
+}
+
+TabContents* NavigationController::GetTabContentsCreateIfNecessary(
+ HWND parent,
+ const NavigationEntry& entry) {
+ TabContents* contents = GetTabContents(entry.GetType());
+ if (!contents) {
+ contents = TabContents::CreateWithType(entry.GetType(), parent, profile_,
+ entry.site_instance());
+ if (!contents->AsWebContents()) {
+ // Update the max page id, otherwise the newly created TabContents may
+ // have reset its max page id resulting in all new navigations. We only
+ // do this for non-WebContents as WebContents takes care of this via its
+ // SiteInstance. If this creation is the result of a restore, WebContents
+ // handles invoking ReservePageIDRange to make sure the renderer's
+ // max_page_id is updated to reflect the restored range of page ids.
+ int32 max_page_id = contents->GetMaxPageID();
+ for (size_t i = 0; i < entries_.size(); ++i) {
+ if (entries_[i]->GetType() == entry.GetType())
+ max_page_id = std::max(max_page_id, entries_[i]->GetPageID());
+ }
+ contents->UpdateMaxPageID(max_page_id);
+ }
+ RegisterTabContents(contents);
+ }
+
+ // We should not be trying to collect this tab contents.
+ DCHECK(tab_contents_collector_map_.find(contents->type()) ==
+ tab_contents_collector_map_.end());
+
+ return contents;
+}
+
+void NavigationController::RegisterTabContents(TabContents* some_contents) {
+ DCHECK(some_contents);
+ TabContentsType t = some_contents->type();
+ TabContents* tc;
+ if ((tc = tab_contents_map_[t]) != some_contents) {
+ if (tc) {
+ NOTREACHED() << "Should not happen. Multiple contents for one type";
+ } else {
+ tab_contents_map_[t] = some_contents;
+ some_contents->set_controller(this);
+ }
+ }
+ if (some_contents->AsDOMUIHost())
+ some_contents->AsDOMUIHost()->AttachMessageHandlers();
+}
+
+SessionService* NavigationController::GetSessionService() const {
+ if (profile_->HasSessionService())
+ return profile_->GetSessionService();
+
+ return NULL;
+}
+
+void NavigationController::SyncSessionWithEntryByPageID(
+ TabContentsType type,
+ SiteInstance *instance,
+ int32 page_id) const {
+ int index = GetEntryIndexWithPageID(type, instance, page_id);
+ if (index != -1)
+ SyncSessionWithEntry(entries_[index], index);
+}
+
+// static
+void NavigationController::DisablePromptOnRepost() {
+ check_for_repost_ = false;
+}
+
+void NavigationController::SetActive(bool is_active) {
+ if (is_active) {
+ if (needs_reload_) {
+ LoadIfNecessary();
+ } else if (load_pending_entry_when_active_) {
+ NavigateToPendingEntry(false);
+ load_pending_entry_when_active_ = false;
+ }
+ }
+}
+
+void NavigationController::LoadIfNecessary() {
+ if (!needs_reload_)
+ return;
+
+ needs_reload_ = false;
+ // Calling Reload() results in ignoring state, and not loading.
+ // Explicitly use NavigateToPendingEntry so that the renderer uses the
+ // cached state.
+ pending_entry_index_ = last_committed_entry_index_;
+ NavigateToPendingEntry(false);
+}
+
+void NavigationController::SyncSessionWithEntry(const NavigationEntry* entry,
+ int index) const {
+ SessionService* session_service = GetSessionService();
+ if (!session_service)
+ return;
+
+ DCHECK(entry && index != -1);
+ if (entry->GetDisplayURL().is_valid())
+ session_service->UpdateTabNavigation(window_id(), session_id(), index,
+ *entry);
+}
+
+void NavigationController::SyncSessionWithSelectedIndex(int index) const {
+ DCHECK(index != -1);
+ SessionService* session_service = GetSessionService();
+ if (!session_service)
+ return;
+
+ session_service->SetSelectedNavigationIndex(window_id(), session_id(), index);
+}
+
+int NavigationController::GetMaxPageID() const {
+ return active_contents_->GetMaxPageID();
+}
+
+NavigationController* NavigationController::Clone(HWND parent_hwnd) {
+ NavigationController* nc = new NavigationController(NULL, profile_);
+
+ if (GetEntryCount() == 0)
+ return nc;
+
+ nc->needs_reload_ = true;
+
+ nc->entries_.reserve(entries_.size());
+ for (int i = 0, c = GetEntryCount(); i < c; ++i)
+ nc->entries_.push_back(new NavigationEntry(*GetEntryAtIndex(i)));
+
+ nc->FinishRestore(parent_hwnd, last_committed_entry_index_);
+
+ return nc;
+}
+
+void NavigationController::ScheduleTabContentsCollection(TabContentsType t) {
+ TabContentsCollectorMap::const_iterator i =
+ tab_contents_collector_map_.find(t);
+
+ // The tab contents is already scheduled for collection.
+ if (i != tab_contents_collector_map_.end())
+ return;
+
+ // If we currently don't have a TabContents for t, skip.
+ if (tab_contents_map_.find(t) == tab_contents_map_.end())
+ return;
+
+ // Create a collector and schedule it.
+ TabContentsCollector* tcc = new TabContentsCollector(this, t);
+ tab_contents_collector_map_[t] = tcc;
+ MessageLoop::current()->PostTask(FROM_HERE, tcc);
+}
+
+void NavigationController::CancelTabContentsCollection(TabContentsType t) {
+ TabContentsCollectorMap::iterator i = tab_contents_collector_map_.find(t);
+
+ if (i != tab_contents_collector_map_.end()) {
+ DCHECK(i->second);
+ i->second->Cancel();
+ tab_contents_collector_map_.erase(i);
+ }
+}
+
+void NavigationController::FinishRestore(HWND parent_hwnd, int selected_index) {
+ DCHECK(selected_index >= 0 && selected_index < GetEntryCount());
+ ConfigureEntriesForRestore(&entries_);
+
+ set_max_restored_page_id(GetEntryCount());
+
+ last_committed_entry_index_ = selected_index;
+
+ // Callers assume we have an active_contents after restoring, so set it now.
+ active_contents_ =
+ GetTabContentsCreateIfNecessary(parent_hwnd, *entries_[selected_index]);
+}