Index: content/browser/frame_host/navigation_controller_impl.cc |
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc |
index 6d1c597d7604629df1b636ed8947c099fbc40c5c..a751ccd3da09dd5b6223138a65e5fe20776e9635 100644 |
--- a/content/browser/frame_host/navigation_controller_impl.cc |
+++ b/content/browser/frame_host/navigation_controller_impl.cc |
@@ -104,24 +104,30 @@ void ConfigureEntriesForRestore( |
} |
} |
-// See NavigationController::IsURLInPageNavigation for how this works and why. |
+// There are two general cases where a navigation is in page: |
+// 1. A fragment navigation, in which the url is kept the same except for the |
+// reference fragment. |
+// 2. A history API navigation (pushState and replaceState). This case is |
+// always in-page, but the urls are not guaranteed to match excluding the |
+// fragment. The relevant spec allows pushState/replaceState to any URL on |
+// the same origin. |
+// However, due to reloads, even identical urls are *not* guaranteed to be |
+// in-page navigations, we have to trust the renderer almost entirely. |
+// The one thing we do know is that cross-origin navigations will *never* be |
+// in-page. Therefore, trust the renderer if the URLs are on the same origin, |
+// and assume the renderer is malicious if a cross-origin navigation claims to |
+// be in-page. |
bool AreURLsInPageNavigation(const GURL& existing_url, |
const GURL& new_url, |
bool renderer_says_in_page, |
- NavigationType navigation_type) { |
- if (existing_url.GetOrigin() == new_url.GetOrigin()) |
- return renderer_says_in_page; |
- |
- if (!new_url.has_ref()) { |
- // When going back from the ref URL to the non ref one the navigation type |
- // is IN_PAGE. |
- return navigation_type == NAVIGATION_TYPE_IN_PAGE; |
- } |
- |
- url::Replacements<char> replacements; |
- replacements.ClearRef(); |
- return existing_url.ReplaceComponents(replacements) == |
- new_url.ReplaceComponents(replacements); |
+ RenderFrameHost* rfh) { |
+ WebPreferences prefs = rfh->GetRenderViewHost()->GetWebkitPreferences(); |
+ bool is_same_origin = existing_url.is_empty() || |
+ existing_url.GetOrigin() == new_url.GetOrigin() || |
+ !prefs.web_security_enabled; |
+ if (!is_same_origin && renderer_says_in_page) |
+ rfh->GetProcess()->ReceivedBadMessage(); |
+ return is_same_origin && renderer_says_in_page; |
} |
// Determines whether or not we should be carrying over a user agent override |
@@ -766,8 +772,8 @@ bool NavigationControllerImpl::RendererDidNavigate( |
details->type = ClassifyNavigation(rfh, params); |
// is_in_page must be computed before the entry gets committed. |
- details->is_in_page = IsURLInPageNavigation( |
- params.url, params.was_within_same_page, details->type); |
+ details->is_in_page = AreURLsInPageNavigation(rfh->GetLastCommittedURL(), |
+ params.url, params.was_within_same_page, rfh); |
switch (details->type) { |
case NAVIGATION_TYPE_NEW_PAGE: |
@@ -986,8 +992,7 @@ NavigationType NavigationControllerImpl::ClassifyNavigation( |
// navigations that don't actually navigate, but it can happen when there is |
// an encoding override (it always sends a navigation request). |
if (AreURLsInPageNavigation(existing_entry->GetURL(), params.url, |
- params.was_within_same_page, |
- NAVIGATION_TYPE_UNKNOWN)) { |
+ params.was_within_same_page, rfh)) { |
return NAVIGATION_TYPE_IN_PAGE; |
} |
@@ -1253,10 +1258,10 @@ int NavigationControllerImpl::GetIndexOfEntry( |
bool NavigationControllerImpl::IsURLInPageNavigation( |
const GURL& url, |
bool renderer_says_in_page, |
- NavigationType navigation_type) const { |
+ RenderFrameHost* rfh) const { |
NavigationEntry* last_committed = GetLastCommittedEntry(); |
return last_committed && AreURLsInPageNavigation( |
- last_committed->GetURL(), url, renderer_says_in_page, navigation_type); |
+ last_committed->GetURL(), url, renderer_says_in_page, rfh); |
} |
void NavigationControllerImpl::CopyStateFrom( |