Index: content/browser/frame_host/navigation_controller_impl_browsertest.cc |
diff --git a/content/browser/frame_host/navigation_controller_impl_browsertest.cc b/content/browser/frame_host/navigation_controller_impl_browsertest.cc |
index 1fd83625b7501502d1c8be4b9b7a388c718a4528..d1a5a7dd3ddac5ec79ab07b253249d1c81e3e410 100644 |
--- a/content/browser/frame_host/navigation_controller_impl_browsertest.cc |
+++ b/content/browser/frame_host/navigation_controller_impl_browsertest.cc |
@@ -17,6 +17,7 @@ |
#include "content/browser/frame_host/navigation_entry_impl.h" |
#include "content/browser/loader/resource_dispatcher_host_impl.h" |
#include "content/browser/web_contents/web_contents_impl.h" |
+#include "content/common/frame_messages.h" |
#include "content/common/page_state_serialization.h" |
#include "content/common/site_isolation_policy.h" |
#include "content/public/browser/navigation_handle.h" |
@@ -5880,6 +5881,90 @@ IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest, |
EXPECT_EQ(start_url.GetOrigin().spec(), origin + "/"); |
} |
+// A BrowserMessageFilter that drops FrameHostMsg_DidCommitProvisionaLoad IPC |
+// message for a specified URL and runs a callback on the UI thread. |
+class CommitMessageFilter : public BrowserMessageFilter { |
+ public: |
+ CommitMessageFilter(const GURL& url, base::Closure on_commit) |
+ : BrowserMessageFilter(FrameMsgStart), url_(url), on_commit_(on_commit) {} |
+ |
+ protected: |
+ ~CommitMessageFilter() override {} |
+ |
+ private: |
+ // BrowserMessageFilter: |
+ bool OnMessageReceived(const IPC::Message& message) override { |
+ if (message.type() != FrameHostMsg_DidCommitProvisionalLoad::ID) |
+ return false; |
+ |
+ // Parse the IPC message so the URL can be checked agains the expected one. |
+ base::PickleIterator iter(message); |
+ FrameHostMsg_DidCommitProvisionalLoad_Params validated_params; |
+ if (!IPC::ParamTraits<FrameHostMsg_DidCommitProvisionalLoad_Params>::Read( |
+ &message, &iter, &validated_params)) { |
+ return false; |
+ } |
+ |
+ // Only handle the message if the URLs are matching. |
+ if (validated_params.url != url_) |
+ return false; |
+ |
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, on_commit_); |
+ return true; |
+ } |
+ |
+ GURL url_; |
+ base::Closure on_commit_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(CommitMessageFilter); |
+}; |
+ |
+// Test which simulates a race condition between a cross-origin, same-process |
+// navigation and a same page session history navigation. When such a race |
+// occurs, the renderer will commit the cross-origin navigation, updating its |
+// version of the current document sequence number, and will send an IPC to the |
+// browser process. The session history navigation comes after the commit for |
+// the cross-origin navigation and updates the URL, but not the origin of the |
+// document. This results in mismatch between the two and causes the renderer |
+// process to be killed. See https://crbug.com/630103. |
+IN_PROC_BROWSER_TEST_F(NavigationControllerBrowserTest, |
+ RaceCrossOriginNavigationAndSamePageHistoryNavigation) { |
+ WebContentsImpl* web_contents = |
+ static_cast<WebContentsImpl*>(shell()->web_contents()); |
+ |
+ // Navigate to a simple page and then perform an in-page navigation. |
+ GURL start_url(embedded_test_server()->GetURL("a.com", "/title1.html")); |
+ EXPECT_TRUE(NavigateToURL(shell(), start_url)); |
+ |
+ GURL same_page_url( |
+ embedded_test_server()->GetURL("a.com", "/title1.html#foo")); |
+ EXPECT_TRUE(NavigateToURL(shell(), same_page_url)); |
+ EXPECT_EQ(2, web_contents->GetController().GetEntryCount()); |
+ |
+ // Create a CommitMessageFilter, which will drop the commit IPC for a |
+ // cross-origin, same process navigation and will perform a GoBack. |
+ GURL cross_origin_url( |
+ embedded_test_server()->GetURL("suborigin.a.com", "/title2.html")); |
+ scoped_refptr<CommitMessageFilter> filter = new CommitMessageFilter( |
+ cross_origin_url, |
+ base::Bind(&NavigationControllerImpl::GoBack, |
+ base::Unretained(&web_contents->GetController()))); |
+ web_contents->GetMainFrame()->GetProcess()->AddFilter(filter.get()); |
+ |
+ // Navigate cross-origin, which will fail and the back navigation should have |
+ // succeeded. |
+ EXPECT_FALSE(NavigateToURL(shell(), cross_origin_url)); |
+ EXPECT_EQ(start_url, web_contents->GetLastCommittedURL()); |
+ EXPECT_EQ(0, web_contents->GetController().GetLastCommittedEntryIndex()); |
+ |
+ // Verify the expected origin through JavaScript. It also has the additional |
+ // verification of the process also being still alive. |
+ std::string origin; |
+ EXPECT_TRUE(ExecuteScriptAndExtractString( |
+ web_contents, "domAutomationController.send(document.origin)", &origin)); |
+ EXPECT_EQ(start_url.GetOrigin().spec(), origin + "/"); |
+} |
+ |
// Test that verifies that Referer and Origin http headers are correctly sent |
// to the final destination of a cross-site POST with a few redirects thrown in. |
// This test is somewhat related to https://crbug.com/635400. |