Index: content/browser/web_contents/navigation_controller_impl.cc |
diff --git a/content/browser/web_contents/navigation_controller_impl.cc b/content/browser/web_contents/navigation_controller_impl.cc |
index c8218af7f0a256d887d420ef1c6c9847cd63331f..5eab596c9fde8466f9ecc636885c37a90589fc2c 100644 |
--- a/content/browser/web_contents/navigation_controller_impl.cc |
+++ b/content/browser/web_contents/navigation_controller_impl.cc |
@@ -288,16 +288,29 @@ void NavigationControllerImpl::ReloadInternal(bool check_for_repost, |
return; |
} |
- DiscardNonCommittedEntriesInternal(); |
- int current_index = GetCurrentEntryIndex(); |
+ NavigationEntryImpl* entry = NULL; |
+ int current_index = -1; |
+ |
+ // If we are reloading the initial navigation, just use the current |
+ // pending entry. Otherwise look up the current entry. |
+ if (IsInitialNavigation() && pending_entry_) { |
+ entry = pending_entry_; |
+ } else { |
+ DiscardNonCommittedEntriesInternal(); |
+ current_index = GetCurrentEntryIndex(); |
+ if (current_index != -1) { |
+ entry = NavigationEntryImpl::FromNavigationEntry( |
+ GetEntryAtIndex(current_index)); |
+ } |
+ } |
+ |
// If we are no where, then we can't reload. TODO(darin): We should add a |
// CanReload method. |
- if (current_index == -1) { |
+ if (!entry) |
return; |
- } |
if (g_check_for_repost && check_for_repost && |
- GetEntryAtIndex(current_index)->GetHasPostData()) { |
+ entry->GetHasPostData()) { |
// The user is asking to reload a page with POST data. Prompt to make sure |
// they really want to do this. If they do, the dialog will call us back |
// with check_for_repost = false. |
@@ -307,11 +320,8 @@ void NavigationControllerImpl::ReloadInternal(bool check_for_repost, |
web_contents_->Activate(); |
web_contents_->GetDelegate()->ShowRepostFormWarningDialog(web_contents_); |
} else { |
- DiscardNonCommittedEntriesInternal(); |
- |
- NavigationEntryImpl* entry = entries_[current_index].get(); |
- SiteInstanceImpl* site_instance = entry->site_instance(); |
- DCHECK(site_instance); |
+ if (!IsInitialNavigation()) |
+ DiscardNonCommittedEntriesInternal(); |
// If we are reloading an entry that no longer belongs to the current |
// site instance (for example, refreshing a page for just installed app), |
@@ -320,6 +330,7 @@ void NavigationControllerImpl::ReloadInternal(bool check_for_repost, |
// as new navigation (which happens to clear forward history). |
// Tabs that are discarded due to low memory conditions may not have a site |
// instance, and should not be treated as a cross-site reload. |
+ SiteInstanceImpl* site_instance = entry->site_instance(); |
if (site_instance && |
site_instance->HasWrongProcessForURL(entry->GetURL())) { |
// Create a navigation entry that resembles the current one, but do not |
@@ -336,15 +347,16 @@ void NavigationControllerImpl::ReloadInternal(bool check_for_repost, |
nav_entry->set_should_replace_entry(true); |
pending_entry_ = nav_entry; |
} else { |
+ pending_entry_ = entry; |
pending_entry_index_ = current_index; |
// The title of the page being reloaded might have been removed in the |
// meanwhile, so we need to revert to the default title upon reload and |
// invalidate the previously cached title (SetTitle will do both). |
// See Chromium issue 96041. |
- entries_[pending_entry_index_]->SetTitle(string16()); |
+ pending_entry_->SetTitle(string16()); |
- entries_[pending_entry_index_]->SetTransitionType(PAGE_TRANSITION_RELOAD); |
+ pending_entry_->SetTransitionType(PAGE_TRANSITION_RELOAD); |
} |
NavigateToPendingEntry(reload_type); |
@@ -392,13 +404,17 @@ void NavigationControllerImpl::LoadEntry(NavigationEntryImpl* 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). |
+ SetPendingEntry(entry); |
+ NavigateToPendingEntry(NO_RELOAD); |
+} |
+ |
+void NavigationControllerImpl::SetPendingEntry(NavigationEntryImpl* entry) { |
DiscardNonCommittedEntriesInternal(); |
pending_entry_ = entry; |
NotificationService::current()->Notify( |
NOTIFICATION_NAV_ENTRY_PENDING, |
Source<NavigationController>(this), |
Details<NavigationEntry>(entry)); |
- NavigateToPendingEntry(NO_RELOAD); |
} |
NavigationEntry* NavigationControllerImpl::GetActiveEntry() const { |
@@ -412,15 +428,37 @@ NavigationEntry* NavigationControllerImpl::GetActiveEntry() const { |
NavigationEntry* NavigationControllerImpl::GetVisibleEntry() const { |
if (transient_entry_index_ != -1) |
return entries_[transient_entry_index_].get(); |
- // Only return the pending_entry for new (non-history), browser-initiated |
- // navigations, in order to prevent URL spoof attacks. |
- // Ideally we would also show the pending entry's URL for new renderer- |
- // initiated navigations with no last committed entry (e.g., a link opening |
- // in a new tab), but an attacker can insert content into the about:blank |
- // page while the pending URL loads in that case. |
- if (pending_entry_ && |
+ // The pending entry is safe to return for new (non-history), browser- |
+ // initiated navigations. Most renderer-initiated navigations should not |
+ // show the pending entry, to prevent URL spoof attacks. |
+ // |
+ // We make an exception for renderer-initiated navigations in new tabs, as |
+ // long as no other page has tried to access the initial empty document in |
+ // the new tab. If another page modifies this blank page, a URL spoof is |
+ // possible, so we must stop showing the pending entry. |
+ RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( |
+ web_contents_->GetRenderViewHost()); |
+ bool safe_to_show_pending = |
+ pending_entry_ && |
+ // Require a new navigation. |
pending_entry_->GetPageID() == -1 && |
+ // Require either browser-initiated or an unmodified new tab. |
+ (!pending_entry_->is_renderer_initiated() || |
+ (IsInitialNavigation() && |
+ !GetLastCommittedEntry() && |
+ !rvh->has_accessed_initial_document())); |
+ |
+ // Also allow showing the pending entry for history navigations in a new tab, |
+ // such as Ctrl+Back. In this case, no existing page is visible and no one |
+ // can script the new tab before it commits. |
+ if (!safe_to_show_pending && |
+ pending_entry_ && |
+ pending_entry_->GetPageID() != -1 && |
+ IsInitialNavigation() && |
!pending_entry_->is_renderer_initiated()) |
+ safe_to_show_pending = true; |
+ |
+ if (safe_to_show_pending) |
return pending_entry_; |
return GetLastCommittedEntry(); |
} |
@@ -1041,6 +1079,7 @@ NavigationType NavigationControllerImpl::ClassifyNavigation( |
// Anything below here we know is a main frame navigation. |
if (pending_entry_ && |
+ !pending_entry_->is_renderer_initiated() && |
existing_entry != pending_entry_ && |
pending_entry_->GetPageID() == -1 && |
existing_entry == GetLastCommittedEntry()) { |