diff options
author | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2015-06-18 14:10:49 +0200 |
---|---|---|
committer | Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com> | 2015-06-18 13:53:24 +0000 |
commit | 813fbf95af77a531c57a8c497345ad2c61d475b3 (patch) | |
tree | 821b2c8de8365f21b6c9ba17a236fb3006a1d506 /chromium/content/browser/frame_host/navigation_controller_impl.cc | |
parent | af6588f8d723931a298c995fa97259bb7f7deb55 (diff) | |
download | qtwebengine-chromium-813fbf95af77a531c57a8c497345ad2c61d475b3.tar.gz |
BASELINE: Update chromium to 44.0.2403.47
Change-Id: Ie056fedba95cf5e5c76b30c4b2c80fca4764aa2f
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>
Diffstat (limited to 'chromium/content/browser/frame_host/navigation_controller_impl.cc')
-rw-r--r-- | chromium/content/browser/frame_host/navigation_controller_impl.cc | 458 |
1 files changed, 344 insertions, 114 deletions
diff --git a/chromium/content/browser/frame_host/navigation_controller_impl.cc b/chromium/content/browser/frame_host/navigation_controller_impl.cc index 4be5a3ce4a0..8339b73051b 100644 --- a/chromium/content/browser/frame_host/navigation_controller_impl.cc +++ b/chromium/content/browser/frame_host/navigation_controller_impl.cc @@ -6,14 +6,16 @@ #include "base/bind.h" #include "base/command_line.h" -#include "base/debug/trace_event.h" #include "base/logging.h" #include "base/metrics/histogram.h" #include "base/strings/string_number_conversions.h" // Temporary #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" +#include "base/trace_event/trace_event.h" #include "cc/base/switches.h" +#include "components/mime_util/mime_util.h" +#include "content/browser/bad_message.h" #include "content/browser/browser_url_handler_impl.h" #include "content/browser/dom_storage/dom_storage_context_wrapper.h" #include "content/browser/dom_storage/session_storage_namespace_impl.h" @@ -37,6 +39,7 @@ #include "content/public/browser/user_metrics.h" #include "content/public/common/content_client.h" #include "content/public/common/content_constants.h" +#include "content/public/common/content_switches.h" #include "net/base/escape.h" #include "net/base/mime_util.h" #include "net/base/net_util.h" @@ -129,9 +132,13 @@ bool AreURLsInPageNavigation(const GURL& existing_url, // for now. existing_url == GURL(url::kAboutBlankURL) || existing_url.GetOrigin() == new_url.GetOrigin() || - !prefs.web_security_enabled; - if (!is_same_origin && renderer_says_in_page) - rfh->GetProcess()->ReceivedBadMessage(); + !prefs.web_security_enabled || + (prefs.allow_universal_access_from_file_urls && + existing_url.SchemeIs(url::kFileScheme)); + if (!is_same_origin && renderer_says_in_page) { + bad_message::ReceivedBadMessage(rfh->GetProcess(), + bad_message::NC_IN_PAGE_NAVIGATION); + } return is_same_origin && renderer_says_in_page; } @@ -163,11 +170,17 @@ NavigationEntry* NavigationController::CreateNavigationEntry( bool is_renderer_initiated, const std::string& extra_headers, BrowserContext* browser_context) { + // Fix up the given URL before letting it be rewritten, so that any minor + // cleanup (e.g., removing leading dots) will not lead to a virtual URL. + GURL dest_url(url); + BrowserURLHandlerImpl::GetInstance()->FixupURLBeforeRewrite(&dest_url, + browser_context); + // Allow the browser URL handler to rewrite the URL. This will, for example, // remove "view-source:" from the beginning of the URL to get the URL that // will actually be loaded. This real URL won't be shown to the user, just // used internally. - GURL loaded_url(url); + GURL loaded_url(dest_url); bool reverse_on_redirect = false; BrowserURLHandlerImpl::GetInstance()->RewriteURLIfNecessary( &loaded_url, browser_context, &reverse_on_redirect); @@ -181,8 +194,8 @@ NavigationEntry* NavigationController::CreateNavigationEntry( base::string16(), transition, is_renderer_initiated); - entry->SetVirtualURL(url); - entry->set_user_typed_url(url); + entry->SetVirtualURL(dest_url); + entry->set_user_typed_url(dest_url); entry->set_update_virtual_url_with_url(reverse_on_redirect); entry->set_extra_headers(extra_headers); return entry; @@ -214,6 +227,8 @@ NavigationControllerImpl::NavigationControllerImpl( BrowserContext* browser_context) : browser_context_(browser_context), pending_entry_(NULL), + failed_pending_entry_id_(0), + failed_pending_entry_should_replace_(false), last_committed_entry_index_(-1), pending_entry_index_(-1), transient_entry_index_(-1), @@ -282,8 +297,7 @@ void NavigationControllerImpl::ReloadInternal(bool check_for_repost, if (transient_entry_index_ != -1) { // If an interstitial is showing, treat a reload as a navigation to the // transient entry's URL. - NavigationEntryImpl* transient_entry = - NavigationEntryImpl::FromNavigationEntry(GetTransientEntry()); + NavigationEntryImpl* transient_entry = GetTransientEntry(); if (!transient_entry) return; LoadURL(transient_entry->GetURL(), @@ -307,8 +321,7 @@ void NavigationControllerImpl::ReloadInternal(bool check_for_repost, DiscardNonCommittedEntriesInternal(); current_index = GetCurrentEntryIndex(); if (current_index != -1) { - entry = NavigationEntryImpl::FromNavigationEntry( - GetEntryAtIndex(current_index)); + entry = GetEntryAtIndex(current_index); } } @@ -424,7 +437,7 @@ void NavigationControllerImpl::SetPendingEntry(NavigationEntryImpl* entry) { Details<NavigationEntry>(entry)); } -NavigationEntry* NavigationControllerImpl::GetActiveEntry() const { +NavigationEntryImpl* NavigationControllerImpl::GetActiveEntry() const { if (transient_entry_index_ != -1) return entries_[transient_entry_index_].get(); if (pending_entry_) @@ -432,7 +445,7 @@ NavigationEntry* NavigationControllerImpl::GetActiveEntry() const { return GetLastCommittedEntry(); } -NavigationEntry* NavigationControllerImpl::GetVisibleEntry() const { +NavigationEntryImpl* NavigationControllerImpl::GetVisibleEntry() const { if (transient_entry_index_ != -1) return entries_[transient_entry_index_].get(); // The pending entry is safe to return for new (non-history), browser- @@ -446,7 +459,7 @@ NavigationEntry* NavigationControllerImpl::GetVisibleEntry() const { bool safe_to_show_pending = pending_entry_ && // Require a new navigation. - pending_entry_->GetPageID() == -1 && + pending_entry_index_ == -1 && // Require either browser-initiated or an unmodified new tab. (!pending_entry_->is_renderer_initiated() || IsUnmodifiedBlankTab()); @@ -455,7 +468,7 @@ NavigationEntry* NavigationControllerImpl::GetVisibleEntry() const { // can script the new tab before it commits. if (!safe_to_show_pending && pending_entry_ && - pending_entry_->GetPageID() != -1 && + pending_entry_index_ != -1 && IsInitialNavigation() && !pending_entry_->is_renderer_initiated()) safe_to_show_pending = true; @@ -473,7 +486,7 @@ int NavigationControllerImpl::GetCurrentEntryIndex() const { return last_committed_entry_index_; } -NavigationEntry* NavigationControllerImpl::GetLastCommittedEntry() const { +NavigationEntryImpl* NavigationControllerImpl::GetLastCommittedEntry() const { if (last_committed_entry_index_ == -1) return NULL; return entries_[last_committed_entry_index_].get(); @@ -481,7 +494,8 @@ NavigationEntry* NavigationControllerImpl::GetLastCommittedEntry() const { bool NavigationControllerImpl::CanViewSource() const { const std::string& mime_type = delegate_->GetContentsMimeType(); - bool is_viewable_mime_type = net::IsSupportedNonImageMimeType(mime_type) && + bool is_viewable_mime_type = + mime_util::IsSupportedNonImageMimeType(mime_type) && !net::IsSupportedMediaMimeType(mime_type); NavigationEntry* visible_entry = GetVisibleEntry(); return visible_entry && !visible_entry->IsViewSourceMode() && @@ -497,12 +511,12 @@ int NavigationControllerImpl::GetEntryCount() const { return static_cast<int>(entries_.size()); } -NavigationEntry* NavigationControllerImpl::GetEntryAtIndex( +NavigationEntryImpl* NavigationControllerImpl::GetEntryAtIndex( int index) const { return entries_.at(index).get(); } -NavigationEntry* NavigationControllerImpl::GetEntryAtOffset( +NavigationEntryImpl* NavigationControllerImpl::GetEntryAtOffset( int offset) const { int index = GetIndexForOffset(offset); if (index < 0 || index >= GetEntryCount()) @@ -655,7 +669,7 @@ void NavigationControllerImpl::LoadURLWithParams(const LoadURLParams& params) { if (HandleDebugURL(params.url, params.transition_type)) { // If Telemetry is running, allow the URL load to proceed as if it's // unhandled, otherwise Telemetry can't tell if Navigation completed. - if (!CommandLine::ForCurrentProcess()->HasSwitch( + if (!base::CommandLine::ForCurrentProcess()->HasSwitch( cc::switches::kEnableGpuBenchmarking)) return; } @@ -719,17 +733,35 @@ void NavigationControllerImpl::LoadURLWithParams(const LoadURLParams& params) { params.is_renderer_initiated, params.extra_headers, browser_context_)); + if (!params.frame_name.empty()) { + // This is only used for navigating subframes in tests. + FrameTreeNode* named_frame = + delegate_->GetFrameTree()->FindByName(params.frame_name); + if (named_frame) + entry->set_frame_tree_node_id(named_frame->frame_tree_node_id()); + } if (params.frame_tree_node_id != -1) entry->set_frame_tree_node_id(params.frame_tree_node_id); + entry->set_source_site_instance( + static_cast<SiteInstanceImpl*>(params.source_site_instance.get())); if (params.redirect_chain.size() > 0) entry->SetRedirectChain(params.redirect_chain); - if (params.should_replace_current_entry) + // Don't allow an entry replacement if there is no entry to replace. + // http://crbug.com/457149 + if (params.should_replace_current_entry && entries_.size() > 0) entry->set_should_replace_entry(true); entry->set_should_clear_history_list(params.should_clear_history_list); entry->SetIsOverridingUserAgent(override); entry->set_transferred_global_request_id( params.transferred_global_request_id); - entry->SetFrameToNavigate(params.frame_name); + +#if defined(OS_ANDROID) + if (params.intent_received_timestamp > 0) { + entry->set_intent_received_timestamp( + base::TimeTicks() + + base::TimeDelta::FromMilliseconds(params.intent_received_timestamp)); + } +#endif switch (params.load_type) { case LOAD_TYPE_DEFAULT: @@ -753,7 +785,7 @@ void NavigationControllerImpl::LoadURLWithParams(const LoadURLParams& params) { } bool NavigationControllerImpl::RendererDidNavigate( - RenderFrameHost* rfh, + RenderFrameHostImpl* rfh, const FrameHostMsg_DidCommitProvisionalLoad_Params& params, LoadCommittedDetails* details) { is_initial_navigation_ = false; @@ -774,14 +806,37 @@ bool NavigationControllerImpl::RendererDidNavigate( // If we are doing a cross-site reload, we need to replace the existing // navigation entry, not add another entry to the history. This has the side - // effect of removing forward browsing history, if such existed. - // Or if we are doing a cross-site redirect navigation, - // we will do a similar thing. - details->did_replace_entry = - pending_entry_ && pending_entry_->should_replace_entry(); + // effect of removing forward browsing history, if such existed. Or if we are + // doing a cross-site redirect navigation, we will do a similar thing. + // + // If this is an error load, we may have already removed the pending entry + // when we got the notice of the load failure. If so, look at the copy of the + // pending parameters that were saved. + if (params.url_is_unreachable && failed_pending_entry_id_ != 0) { + details->did_replace_entry = failed_pending_entry_should_replace_; + } else { + details->did_replace_entry = pending_entry_ && + pending_entry_->should_replace_entry(); + } // Do navigation-type specific actions. These will make and commit an entry. details->type = ClassifyNavigation(rfh, params); +#if DCHECK_IS_ON() + // For site-per-process, both ClassifyNavigation methods get it wrong (see + // http://crbug.com/464014) so don't worry about a mismatch if that's the + // case. + if (!base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kSitePerProcess)) { + NavigationType new_type = ClassifyNavigationWithoutPageID(rfh, params); + // There's constant disagreements over SAME_PAGE between the two classifiers + // so ignore disagreements if that's the case. Otherwise, enforce agreement. + // TODO(avi): Work this out. + if (details->type != NAVIGATION_TYPE_SAME_PAGE && + new_type != NAVIGATION_TYPE_SAME_PAGE) { + DCHECK_EQ(details->type, new_type); + } + } +#endif // DCHECK_IS_ON() // is_in_page must be computed before the entry gets committed. details->is_in_page = AreURLsInPageNavigation(rfh->GetLastCommittedURL(), @@ -838,8 +893,7 @@ bool NavigationControllerImpl::RendererDidNavigate( // All committed entries should have nonempty content state so WebKit doesn't // get confused when we go back to them (see the function for details). DCHECK(params.page_state.IsValid()); - NavigationEntryImpl* active_entry = - NavigationEntryImpl::FromNavigationEntry(GetLastCommittedEntry()); + NavigationEntryImpl* active_entry = GetLastCommittedEntry(); active_entry->SetTimestamp(timestamp); active_entry->SetHttpStatusCode(params.http_status_code); active_entry->SetPageState(params.page_state); @@ -865,8 +919,7 @@ bool NavigationControllerImpl::RendererDidNavigate( // Remember the bindings the renderer process has at this point, so that // we do not grant this entry additional bindings if we come back to it. - active_entry->SetBindings( - static_cast<RenderFrameHostImpl*>(rfh)->GetEnabledBindings()); + active_entry->SetBindings(rfh->GetEnabledBindings()); // Now prep the rest of the details for the notification and broadcast. details->entry = active_entry; @@ -880,15 +933,15 @@ bool NavigationControllerImpl::RendererDidNavigate( } NavigationType NavigationControllerImpl::ClassifyNavigation( - RenderFrameHost* rfh, + RenderFrameHostImpl* rfh, const FrameHostMsg_DidCommitProvisionalLoad_Params& params) const { if (params.page_id == -1) { - // TODO(nasko, creis): An out-of-process child frame has no way of - // knowing the page_id of its parent, so it is passing back -1. The - // semantics here should be re-evaluated during session history refactor - // (see http://crbug.com/236848). For now, we assume this means the - // child frame loaded and proceed. Note that this may do the wrong thing - // for cross-process AUTO_SUBFRAME navigations. + // TODO(nasko, creis): An out-of-process child frame has no way of knowing + // the page_id of its parent, so it is passing back -1. The semantics here + // should be re-evaluated during session history refactor (see + // http://crbug.com/236848 and in particular http://crbug.com/464014). For + // now, we assume this means the child frame loaded and proceed. Note that + // this may do the wrong thing for cross-process AUTO_SUBFRAME navigations. if (rfh->IsCrossProcessSubframe()) return NAVIGATION_TYPE_NEW_SUBFRAME; @@ -977,8 +1030,7 @@ NavigationType NavigationControllerImpl::ClassifyNavigation( temp.append(","); } GURL url(temp); - static_cast<RenderFrameHostImpl*>(rfh)->render_view_host()->Send( - new ViewMsg_TempCrashWithData(url)); + rfh->render_view_host()->Send(new ViewMsg_TempCrashWithData(url)); return NAVIGATION_TYPE_NAV_IGNORE; } NavigationEntryImpl* existing_entry = entries_[existing_entry_index].get(); @@ -996,15 +1048,31 @@ NavigationType NavigationControllerImpl::ClassifyNavigation( !pending_entry_->is_renderer_initiated() && existing_entry != pending_entry_ && pending_entry_->GetPageID() == -1 && - existing_entry == GetLastCommittedEntry()) { - // In this case, we have a pending entry for a URL but WebCore didn't do a - // new navigation. This happens when you press enter in the URL bar to - // reload. We will create a pending entry, but WebKit will convert it to - // a reload since it's the same page and not create a new entry for it - // (the user doesn't want to have a new back/forward entry when they do - // this). If this matches the last committed entry, we want to just ignore - // the pending entry and go back to where we were (the "existing entry"). - return NAVIGATION_TYPE_SAME_PAGE; + existing_entry == GetLastCommittedEntry() && + !params.was_within_same_page) { + // In order to prevent unrelated pending entries from interfering with + // this classification, make sure that the URL committed matches the URLs + // of both the existing entry and the pending entry. There might have been + // a redirection, though, so allow both the existing and pending entries + // to match either the final URL that committed, or the original one + // before redirection. + GURL original_url; + if (params.redirects.size()) + original_url = params.redirects[0]; + + if ((params.url == existing_entry->GetURL() || + original_url == existing_entry->GetURL()) && + (params.url == pending_entry_->GetURL() || + original_url == pending_entry_->GetURL())) { + // In this case, we have a pending entry for a URL but Blink didn't do a + // new navigation. This happens when you press enter in the URL bar to + // reload. We will create a pending entry, but Blink will convert it to a + // reload since it's the same page and not create a new entry for it (the + // user doesn't want to have a new back/forward entry when they do this). + // If this matches the last committed entry, we want to just ignore the + // pending entry and go back to where we were (the "existing entry"). + return NAVIGATION_TYPE_SAME_PAGE; + } } // Any toplevel navigations with the same base (minus the reference fragment) @@ -1022,8 +1090,123 @@ NavigationType NavigationControllerImpl::ClassifyNavigation( return NAVIGATION_TYPE_EXISTING_PAGE; } +NavigationType NavigationControllerImpl::ClassifyNavigationWithoutPageID( + RenderFrameHostImpl* rfh, + const FrameHostMsg_DidCommitProvisionalLoad_Params& params) const { + if (params.did_create_new_entry) { + // A new entry. We may or may not have a pending entry for the page, and + // this may or may not be the main frame. + if (ui::PageTransitionIsMainFrame(params.transition)) { + // TODO(avi): I want to use |if (!rfh->GetParent())| here but lots of unit + // tests fake auto subframe commits by sending the main frame a + // PAGE_TRANSITION_AUTO_SUBFRAME transition. Fix those, and adjust here. + return NAVIGATION_TYPE_NEW_PAGE; + } + + // When this is a new subframe navigation, we should have a committed page + // in which it's a subframe. This may not be the case when an iframe is + // navigated on a popup navigated to about:blank (the iframe would be + // written into the popup by script on the main page). For these cases, + // there isn't any navigation stuff we can do, so just ignore it. + if (!GetLastCommittedEntry()) + return NAVIGATION_TYPE_NAV_IGNORE; + + // Valid subframe navigation. + return NAVIGATION_TYPE_NEW_SUBFRAME; + } + + // We only clear the session history when navigating to a new page. + DCHECK(!params.history_list_was_cleared); + + if (!ui::PageTransitionIsMainFrame(params.transition)) { + // All manual subframes would be did_create_new_entry and handled above, so + // we know this is auto. + if (GetLastCommittedEntry()) { + return NAVIGATION_TYPE_AUTO_SUBFRAME; + } else { + // We ignore subframes created in non-committed pages; we'd appreciate if + // people stopped doing that. + return NAVIGATION_TYPE_NAV_IGNORE; + } + } + + if (params.nav_entry_id == 0) { + // This is a renderer-initiated navigation (nav_entry_id == 0), but didn't + // create a new page. + + // Just like above in the did_create_new_entry case, it's possible to + // scribble onto an uncommitted page. Again, there isn't any navigation + // stuff that we can do, so ignore it here as well. + if (!GetLastCommittedEntry()) + return NAVIGATION_TYPE_NAV_IGNORE; + + if (params.was_within_same_page) { + // This is history.replaceState(), which is renderer-initiated yet within + // the same page. + return NAVIGATION_TYPE_IN_PAGE; + } else { + // This is history.reload() or a client-side redirect. + return NAVIGATION_TYPE_EXISTING_PAGE; + } + } + + if (pending_entry_ && pending_entry_index_ == -1 && + pending_entry_->GetUniqueID() == params.nav_entry_id) { + // In this case, we have a pending entry for a load of a new URL but Blink + // didn't do a new navigation (params.did_create_new_entry). This happens + // when you press enter in the URL bar to reload. We will create a pending + // entry, but Blink will convert it to a reload since it's the same page and + // not create a new entry for it (the user doesn't want to have a new + // back/forward entry when they do this). Therefore we want to just ignore + // the pending entry and go back to where we were (the "existing entry"). + return NAVIGATION_TYPE_SAME_PAGE; + } + + if (params.intended_as_new_entry) { + // This was intended to be a navigation to a new entry but the pending entry + // got cleared in the meanwhile. Classify as EXISTING_PAGE because we may or + // may not have a pending entry. + return NAVIGATION_TYPE_EXISTING_PAGE; + } + + if (params.url_is_unreachable && failed_pending_entry_id_ != 0 && + params.nav_entry_id == failed_pending_entry_id_) { + // If the renderer was going to a new pending entry that got cleared because + // of an error, this is the case of the user trying to retry a failed load + // by pressing return. Classify as EXISTING_PAGE because we probably don't + // have a pending entry. + return NAVIGATION_TYPE_EXISTING_PAGE; + } + + // Now we know that the notification is for an existing page. Find that entry. + int existing_entry_index = GetEntryIndexWithUniqueID(params.nav_entry_id); + if (existing_entry_index == -1) { + // The page was not found. It could have been pruned because of the limit on + // back/forward entries (not likely since we'll usually tell it to navigate + // to such entries). It could also mean that the renderer is smoking crack. + // TODO(avi): Crash the renderer like we do in the old ClassifyNavigation? + NOTREACHED() << "Could not find nav entry with id " << params.nav_entry_id; + return NAVIGATION_TYPE_NAV_IGNORE; + } + + // Any top-level navigations with the same base (minus the reference fragment) + // are in-page navigations. (We weeded out subframe navigations above.) Most + // of the time this doesn't matter since Blink doesn't tell us about subframe + // navigations that don't actually navigate, but it can happen when there is + // an encoding override (it always sends a navigation request). + NavigationEntryImpl* existing_entry = entries_[existing_entry_index].get(); + if (AreURLsInPageNavigation(existing_entry->GetURL(), params.url, + params.was_within_same_page, rfh)) { + return NAVIGATION_TYPE_IN_PAGE; + } + + // Since we weeded out "new" navigations above, we know this is an existing + // (back/forward) navigation. + return NAVIGATION_TYPE_EXISTING_PAGE; +} + void NavigationControllerImpl::RendererDidNavigateToNewPage( - RenderFrameHost* rfh, + RenderFrameHostImpl* rfh, const FrameHostMsg_DidCommitProvisionalLoad_Params& params, bool replace_entry) { NavigationEntryImpl* new_entry; @@ -1034,12 +1217,8 @@ void NavigationControllerImpl::RendererDidNavigateToNewPage( if (pending_entry_ && (!pending_entry_->site_instance() || pending_entry_->site_instance() == rfh->GetSiteInstance())) { - new_entry = new NavigationEntryImpl(*pending_entry_); + new_entry = pending_entry_->Clone(); - // Don't use the page type from the pending entry. Some interstitial page - // may have set the type to interstitial. Once we commit, however, the page - // type must always be normal. - new_entry->set_page_type(PAGE_TYPE_NORMAL); update_virtual_url = new_entry->update_virtual_url_with_url(); } else { new_entry = new NavigationEntryImpl; @@ -1060,8 +1239,11 @@ void NavigationControllerImpl::RendererDidNavigateToNewPage( update_virtual_url = needs_update; } - if (params.url_is_unreachable) - new_entry->set_page_type(PAGE_TYPE_ERROR); + // Don't use the page type from the pending entry. Some interstitial page + // may have set the type to interstitial. Once we commit, however, the page + // type must always be normal or error. + new_entry->set_page_type(params.url_is_unreachable ? PAGE_TYPE_ERROR + : PAGE_TYPE_NORMAL); new_entry->SetURL(params.url); if (update_virtual_url) UpdateVirtualURLToURL(new_entry, params.url); @@ -1097,7 +1279,7 @@ void NavigationControllerImpl::RendererDidNavigateToNewPage( } void NavigationControllerImpl::RendererDidNavigateToExistingPage( - RenderFrameHost* rfh, + RenderFrameHostImpl* rfh, const FrameHostMsg_DidCommitProvisionalLoad_Params& params) { // We should only get here for main frame navigations. DCHECK(ui::PageTransitionIsMainFrame(params.transition)); @@ -1112,6 +1294,8 @@ void NavigationControllerImpl::RendererDidNavigateToExistingPage( NavigationEntryImpl* entry = entries_[entry_index].get(); // The URL may have changed due to redirects. + entry->set_page_type(params.url_is_unreachable ? PAGE_TYPE_ERROR + : PAGE_TYPE_NORMAL); entry->SetURL(params.url); entry->SetReferrer(params.referrer); if (entry->update_virtual_url_with_url()) @@ -1150,7 +1334,7 @@ void NavigationControllerImpl::RendererDidNavigateToExistingPage( } void NavigationControllerImpl::RendererDidNavigateToSamePage( - RenderFrameHost* rfh, + RenderFrameHostImpl* rfh, const FrameHostMsg_DidCommitProvisionalLoad_Params& params) { // This mode implies we have a pending entry that's the same as an existing // entry for this page ID. This entry is guaranteed to exist by @@ -1164,6 +1348,8 @@ void NavigationControllerImpl::RendererDidNavigateToSamePage( existing_entry->set_unique_id(pending_entry_->GetUniqueID()); // The URL may have changed due to redirects. + existing_entry->set_page_type(params.url_is_unreachable ? PAGE_TYPE_ERROR + : PAGE_TYPE_NORMAL); if (existing_entry->update_virtual_url_with_url()) UpdateVirtualURLToURL(existing_entry, params.url); existing_entry->SetURL(params.url); @@ -1177,7 +1363,7 @@ void NavigationControllerImpl::RendererDidNavigateToSamePage( } void NavigationControllerImpl::RendererDidNavigateInPage( - RenderFrameHost* rfh, + RenderFrameHostImpl* rfh, const FrameHostMsg_DidCommitProvisionalLoad_Params& params, bool* did_replace_entry) { DCHECK(ui::PageTransitionIsMainFrame(params.transition)) << @@ -1190,6 +1376,8 @@ void NavigationControllerImpl::RendererDidNavigateInPage( // entry and it will be the same page as the new navigation (minus the // reference fragments, of course). We'll update the URL of the existing // entry without pruning the forward history. + existing_entry->set_page_type(params.url_is_unreachable ? PAGE_TYPE_ERROR + : PAGE_TYPE_NORMAL); existing_entry->SetURL(params.url); if (existing_entry->update_virtual_url_with_url()) UpdateVirtualURLToURL(existing_entry, params.url); @@ -1209,11 +1397,26 @@ void NavigationControllerImpl::RendererDidNavigateInPage( } void NavigationControllerImpl::RendererDidNavigateNewSubframe( - RenderFrameHost* rfh, + RenderFrameHostImpl* rfh, const FrameHostMsg_DidCommitProvisionalLoad_Params& params) { - if (ui::PageTransitionCoreTypeIs(params.transition, - ui::PAGE_TRANSITION_AUTO_SUBFRAME)) { - // This is not user-initiated. Ignore. + if (!ui::PageTransitionCoreTypeIs(params.transition, + ui::PAGE_TRANSITION_MANUAL_SUBFRAME)) { + // There was a comment here that said, "This is not user-initiated. Ignore." + // But this makes no sense; non-user-initiated navigations should be + // determined to be of type NAVIGATION_TYPE_AUTO_SUBFRAME and sent to + // RendererDidNavigateAutoSubframe below. + // + // This if clause dates back to https://codereview.chromium.org/115919 and + // the handling of immediate redirects. TODO(avi): Is this still valid? I'm + // pretty sure that's there's nothing left of that code and that we should + // take this out. + // + // Except for cross-process iframes; this doesn't work yet for them. + if (!base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kSitePerProcess)) { + NOTREACHED(); + } + DiscardNonCommittedEntriesInternal(); return; } @@ -1224,15 +1427,17 @@ void NavigationControllerImpl::RendererDidNavigateNewSubframe( // band with the actual navigations. DCHECK(GetLastCommittedEntry()) << "ClassifyNavigation should guarantee " << "that a last committed entry exists."; - NavigationEntryImpl* new_entry = new NavigationEntryImpl( - *NavigationEntryImpl::FromNavigationEntry(GetLastCommittedEntry())); + NavigationEntryImpl* new_entry = GetLastCommittedEntry()->Clone(); new_entry->SetPageID(params.page_id); InsertOrReplaceEntry(new_entry, false); } bool NavigationControllerImpl::RendererDidNavigateAutoSubframe( - RenderFrameHost* rfh, + RenderFrameHostImpl* rfh, const FrameHostMsg_DidCommitProvisionalLoad_Params& params) { + DCHECK(ui::PageTransitionCoreTypeIs(params.transition, + ui::PAGE_TRANSITION_AUTO_SUBFRAME)); + // We're guaranteed to have a previously committed entry, and we now need to // handle navigation inside of a subframe in it without creating a new entry. DCHECK(GetLastCommittedEntry()); @@ -1251,11 +1456,32 @@ bool NavigationControllerImpl::RendererDidNavigateAutoSubframe( // Update the current navigation entry in case we're going back/forward. if (entry_index != last_committed_entry_index_) { + // Make sure that a subframe commit isn't changing the main frame's origin. + // Otherwise the renderer process may be confused, leading to a URL spoof. + // We can't check the path since that may change (https://crbug.com/373041). + if (GetLastCommittedEntry()->GetURL().GetOrigin() != + GetEntryAtIndex(entry_index)->GetURL().GetOrigin()) { + // TODO(creis): This is unexpectedly being encountered in practice. If + // you encounter this in practice, please post details to + // https://crbug.com/486916. Once that's resolved, we'll change this to + // kill the renderer process with bad_message::NC_AUTO_SUBFRAME. + NOTREACHED() << "Unexpected main frame origin change on AUTO_SUBFRAME."; + } last_committed_entry_index_ = entry_index; DiscardNonCommittedEntriesInternal(); return true; } + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kSitePerProcess)) { + // This may be a "new auto" case where we add a new FrameNavigationEntry, or + // it may be a "history auto" case where we update an existing one. + NavigationEntryImpl* last_committed = GetLastCommittedEntry(); + last_committed->AddOrUpdateFrameEntry(rfh->frame_tree_node(), + rfh->GetSiteInstance(), params.url, + params.referrer); + } + // We do not need to discard the pending entry in this case, since we will // not generate commit notifications for this auto-subframe navigation. return false; @@ -1317,19 +1543,8 @@ void NavigationControllerImpl::CopyStateFromAndPrune( NavigationControllerImpl* source = static_cast<NavigationControllerImpl*>(temp); - // The SiteInstance and page_id of the last committed entry needs to be - // remembered at this point, in case there is only one committed entry - // and it is pruned. We use a scoped_refptr to ensure the SiteInstance - // can't be freed during this time period. - NavigationEntryImpl* last_committed = - NavigationEntryImpl::FromNavigationEntry(GetLastCommittedEntry()); - scoped_refptr<SiteInstance> site_instance( - last_committed->site_instance()); - int32 minimum_page_id = last_committed->GetPageID(); - int32 max_page_id = - delegate_->GetMaxPageIDForSiteInstance(site_instance.get()); - - // Remove all the entries leaving the active entry. + + // Remove all the entries leaving the last committed entry. PruneAllButLastCommittedInternal(); // We now have one entry, possibly with a new pending entry. Ensure that @@ -1339,7 +1554,7 @@ void NavigationControllerImpl::CopyStateFromAndPrune( source->PruneOldestEntryIfFull(); // Insert the entries from source. Don't use source->GetCurrentEntryIndex as - // we don't want to copy over the transient entry. Ignore any pending entry, + // we don't want to copy over the transient entry. Ignore any pending entry, // since it has not committed in source. int max_source_index = source->last_committed_entry_index_; if (max_source_index == -1) @@ -1358,22 +1573,19 @@ void NavigationControllerImpl::CopyStateFromAndPrune( // Adjust indices such that the last entry and pending are at the end now. last_committed_entry_index_ = GetEntryCount() - 1; - delegate_->SetHistoryLengthAndPrune(site_instance.get(), - max_source_index, - minimum_page_id); + delegate_->SetHistoryOffsetAndLength(last_committed_entry_index_, + GetEntryCount()); - // Copy the max page id map from the old tab to the new tab. This ensures - // that new and existing navigations in the tab's current SiteInstances - // are identified properly. + // Copy the max page id map from the old tab to the new tab. This ensures that + // new and existing navigations in the tab's current SiteInstances are + // identified properly. + NavigationEntryImpl* last_committed = GetLastCommittedEntry(); + int32 site_max_page_id = + delegate_->GetMaxPageIDForSiteInstance(last_committed->site_instance()); delegate_->CopyMaxPageIDsFrom(source->delegate()->GetWebContents()); + delegate_->UpdateMaxPageIDForSiteInstance(last_committed->site_instance(), + site_max_page_id); max_restored_page_id_ = source->max_restored_page_id_; - - // If there is a last committed entry, be sure to include it in the new - // max page ID map. - if (max_page_id > -1) { - delegate_->UpdateMaxPageIDForSiteInstance(site_instance.get(), - max_page_id); - } } bool NavigationControllerImpl::CanPruneAllButLastCommitted() { @@ -1400,18 +1612,11 @@ bool NavigationControllerImpl::CanPruneAllButLastCommitted() { void NavigationControllerImpl::PruneAllButLastCommitted() { PruneAllButLastCommittedInternal(); - // We should still have a last committed entry. - DCHECK_NE(-1, last_committed_entry_index_); + DCHECK_EQ(0, last_committed_entry_index_); + DCHECK_EQ(1, GetEntryCount()); - // We pass 0 instead of GetEntryCount() for the history_length parameter of - // SetHistoryLengthAndPrune, because it will create history_length additional - // history entries. - // TODO(jochen): This API is confusing and we should clean it up. - // http://crbug.com/178491 - NavigationEntryImpl* entry = - NavigationEntryImpl::FromNavigationEntry(GetVisibleEntry()); - delegate_->SetHistoryLengthAndPrune( - entry->site_instance(), 0, entry->GetPageID()); + delegate_->SetHistoryOffsetAndLength(last_committed_entry_index_, + GetEntryCount()); } void NavigationControllerImpl::PruneAllButLastCommittedInternal() { @@ -1509,6 +1714,11 @@ bool NavigationControllerImpl::NeedsReload() const { void NavigationControllerImpl::SetNeedsReload() { needs_reload_ = true; + + if (last_committed_entry_index_ != -1) { + entries_[last_committed_entry_index_]->SetTransitionType( + ui::PAGE_TRANSITION_RELOAD); + } } void NavigationControllerImpl::RemoveEntryAtIndexInternal(int index) { @@ -1533,7 +1743,7 @@ void NavigationControllerImpl::DiscardNonCommittedEntries() { } } -NavigationEntry* NavigationControllerImpl::GetPendingEntry() const { +NavigationEntryImpl* NavigationControllerImpl::GetPendingEntry() const { return pending_entry_; } @@ -1556,6 +1766,7 @@ void NavigationControllerImpl::InsertOrReplaceEntry(NavigationEntryImpl* entry, DiscardNonCommittedEntriesInternal(); int current_size = static_cast<int>(entries_.size()); + DCHECK_IMPLIES(replace, current_size > 0); if (current_size > 0) { // Prune any entries which are in front of the current entry. @@ -1720,17 +1931,25 @@ void NavigationControllerImpl::FinishRestore(int selected_index, } void NavigationControllerImpl::DiscardNonCommittedEntriesInternal() { - DiscardPendingEntry(); + DiscardPendingEntry(false); DiscardTransientEntry(); } -void NavigationControllerImpl::DiscardPendingEntry() { +void NavigationControllerImpl::DiscardPendingEntry(bool was_failure) { // It is not safe to call DiscardPendingEntry while NavigateToEntry is in // progress, since this will cause a use-after-free. (We only allow this // when the tab is being destroyed for shutdown, since it won't return to // NavigateToEntry in that case.) http://crbug.com/347742. CHECK(!in_navigate_to_pending_entry_ || delegate_->IsBeingDestroyed()); + if (was_failure && pending_entry_) { + failed_pending_entry_id_ = pending_entry_->GetUniqueID(); + failed_pending_entry_should_replace_ = + pending_entry_->should_replace_entry(); + } else { + failed_pending_entry_id_ = 0; + } + if (pending_entry_index_ == -1) delete pending_entry_; pending_entry_ = NULL; @@ -1756,7 +1975,16 @@ int NavigationControllerImpl::GetEntryIndexWithPageID( return -1; } -NavigationEntry* NavigationControllerImpl::GetTransientEntry() const { +int NavigationControllerImpl::GetEntryIndexWithUniqueID( + int nav_entry_id) const { + for (int i = static_cast<int>(entries_.size()) - 1; i >= 0; --i) { + if (entries_[i]->GetUniqueID() == nav_entry_id) + return i; + } + return -1; +} + +NavigationEntryImpl* NavigationControllerImpl::GetTransientEntry() const { if (transient_entry_index_ == -1) return NULL; return entries_[transient_entry_index_].get(); @@ -1781,12 +2009,14 @@ void NavigationControllerImpl::InsertEntriesFrom( DCHECK_LE(max_index, source.GetEntryCount()); size_t insert_index = 0; for (int i = 0; i < max_index; i++) { - // When cloning a tab, copy all entries except interstitial pages - if (source.entries_[i].get()->GetPageType() != - PAGE_TYPE_INTERSTITIAL) { + // When cloning a tab, copy all entries except interstitial pages. + if (source.entries_[i].get()->GetPageType() != PAGE_TYPE_INTERSTITIAL) { + // TODO(creis): Once we start sharing FrameNavigationEntries between + // NavigationEntries, it will not be safe to share them with another tab. + // Must have a version of Clone that recreates them. entries_.insert(entries_.begin() + insert_index++, linked_ptr<NavigationEntryImpl>( - new NavigationEntryImpl(*source.entries_[i]))); + source.entries_[i]->Clone())); } } } |