| Index: content/browser/site_per_process_browsertest.cc | 
| diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc | 
| index 9e312c8d05b019ba59e80d3c3acd6d91b5aca4b4..bd1aceddff64296fc75fedd50454b2b78ae2beef 100644 | 
| --- a/content/browser/site_per_process_browsertest.cc | 
| +++ b/content/browser/site_per_process_browsertest.cc | 
| @@ -55,6 +55,7 @@ | 
| #include "ipc/ipc_security_test_util.h" | 
| #include "net/dns/mock_host_resolver.h" | 
| #include "net/test/embedded_test_server/embedded_test_server.h" | 
| +#include "testing/gtest/include/gtest/gtest.h" | 
| #include "third_party/WebKit/public/web/WebInputEvent.h" | 
| #include "third_party/WebKit/public/web/WebSandboxFlags.h" | 
| #include "ui/display/display_switches.h" | 
| @@ -6332,6 +6333,219 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, | 
| } | 
| } | 
|  | 
| +// Test that a cross-origin frame's navigation can be blocked by CSP frame-src. | 
| +// In this version of a test, CSP comes from HTTP headers. | 
| +IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, | 
| +                       CrossSiteIframeBlockedByParentCSPFromHeaders) { | 
| +  GURL main_url( | 
| +      embedded_test_server()->GetURL("a.com", "/frame-src-self-and-b.html")); | 
| +  EXPECT_TRUE(NavigateToURL(shell(), main_url)); | 
| + | 
| +  FrameTreeNode* root = web_contents()->GetFrameTree()->root(); | 
| + | 
| +  // Sanity-check that the test page has the expected shape for testing. | 
| +  GURL old_subframe_url( | 
| +      embedded_test_server()->GetURL("b.com", "/title2.html")); | 
| +  EXPECT_FALSE(root->child_at(0)->HasSameOrigin(*root)); | 
| +  EXPECT_EQ(old_subframe_url, root->child_at(0)->current_url()); | 
| +  const std::vector<ContentSecurityPolicyHeader>& root_csp = | 
| +      root->current_replication_state().accumulated_csp_headers; | 
| +  EXPECT_EQ(1u, root_csp.size()); | 
| +  EXPECT_EQ("frame-src 'self' http://b.com:*", root_csp[0].header_value); | 
| + | 
| +  // Monitor subframe's load events via main frame's title. | 
| +  EXPECT_TRUE(ExecuteScript(shell()->web_contents(), | 
| +                            "document.querySelector('iframe').onload = " | 
| +                            "    function() { document.title = 'loaded'; };")); | 
| +  EXPECT_TRUE( | 
| +      ExecuteScript(shell()->web_contents(), "document.title = 'not loaded';")); | 
| +  base::string16 expected_title(base::UTF8ToUTF16("loaded")); | 
| +  TitleWatcher title_watcher(shell()->web_contents(), expected_title); | 
| + | 
| +  // Try to navigate the subframe to a blocked URL. | 
| +  TestNavigationObserver load_observer(shell()->web_contents()); | 
| +  GURL blocked_url = embedded_test_server()->GetURL("c.com", "/title3.html"); | 
| +  EXPECT_TRUE( | 
| +      ExecuteScript(root->child_at(0)->current_frame_host(), | 
| +                    "window.location.href = '" + blocked_url.spec() + "';")); | 
| + | 
| +  // The blocked frame should still fire a load event in its parent's process. | 
| +  EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle()); | 
| + | 
| +  // Check that the current RenderFrameHost has stopped loading. | 
| +  if (root->child_at(0)->current_frame_host()->is_loading()) { | 
| +    ADD_FAILURE() << "Blocked RenderFrameHost shouldn't be loading anything"; | 
| +    load_observer.Wait(); | 
| +  } | 
| + | 
| +  // The blocked frame should stay at the old location. | 
| +  EXPECT_EQ(old_subframe_url, root->child_at(0)->current_url()); | 
| + | 
| +  // The blocked frame should keep the old title. | 
| +  std::string frame_title; | 
| +  EXPECT_TRUE(ExecuteScriptAndExtractString( | 
| +      root->child_at(0)->current_frame_host(), | 
| +      "domAutomationController.send(document.title)", &frame_title)); | 
| +  EXPECT_EQ("Title Of Awesomeness", frame_title); | 
| + | 
| +  // Navigate to a URL without CSP. | 
| +  EXPECT_TRUE(NavigateToURL( | 
| +      shell(), embedded_test_server()->GetURL("a.com", "/title1.html"))); | 
| + | 
| +  // Verify that the frame's CSP got correctly reset to an empty set. | 
| +  EXPECT_EQ(0u, | 
| +            root->current_replication_state().accumulated_csp_headers.size()); | 
| +} | 
| + | 
| +// Test that a cross-origin frame's navigation can be blocked by CSP frame-src. | 
| +// In this version of a test, CSP comes from a <meta> element added after the | 
| +// page has already loaded. | 
| +IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, | 
| +                       CrossSiteIframeBlockedByParentCSPFromMeta) { | 
| +  GURL main_url(embedded_test_server()->GetURL( | 
| +      "a.com", "/cross_site_iframe_factory.html?a(a)")); | 
| +  EXPECT_TRUE(NavigateToURL(shell(), main_url)); | 
| + | 
| +  FrameTreeNode* root = web_contents()->GetFrameTree()->root(); | 
| + | 
| +  // Navigate the subframe to a location we will disallow in the future. | 
| +  GURL old_subframe_url( | 
| +      embedded_test_server()->GetURL("b.com", "/title2.html")); | 
| +  NavigateFrameToURL(root->child_at(0), old_subframe_url); | 
| + | 
| +  // Add frame-src CSP via a new <meta> element. | 
| +  EXPECT_TRUE(ExecuteScript( | 
| +      shell()->web_contents(), | 
| +      "var meta = document.createElement('meta');" | 
| +      "meta.httpEquiv = 'Content-Security-Policy';" | 
| +      "meta.content = 'frame-src https://a.com:*';" | 
| +      "document.getElementsByTagName('head')[0].appendChild(meta);")); | 
| + | 
| +  // Sanity-check that the test page has the expected shape for testing. | 
| +  // (the CSP should not have an effect on the already loaded frames). | 
| +  EXPECT_FALSE(root->child_at(0)->HasSameOrigin(*root)); | 
| +  EXPECT_EQ(old_subframe_url, root->child_at(0)->current_url()); | 
| +  const std::vector<ContentSecurityPolicyHeader>& root_csp = | 
| +      root->current_replication_state().accumulated_csp_headers; | 
| +  EXPECT_EQ(1u, root_csp.size()); | 
| +  EXPECT_EQ("frame-src https://a.com:*", root_csp[0].header_value); | 
| + | 
| +  // Monitor subframe's load events via main frame's title. | 
| +  EXPECT_TRUE(ExecuteScript(shell()->web_contents(), | 
| +                            "document.querySelector('iframe').onload = " | 
| +                            "    function() { document.title = 'loaded'; };")); | 
| +  EXPECT_TRUE( | 
| +      ExecuteScript(shell()->web_contents(), "document.title = 'not loaded';")); | 
| +  base::string16 expected_title(base::UTF8ToUTF16("loaded")); | 
| +  TitleWatcher title_watcher(shell()->web_contents(), expected_title); | 
| + | 
| +  // Try to navigate the subframe to a blocked URL. | 
| +  TestNavigationObserver load_observer2(shell()->web_contents()); | 
| +  GURL blocked_url = embedded_test_server()->GetURL("c.com", "/title3.html"); | 
| +  EXPECT_TRUE( | 
| +      ExecuteScript(root->child_at(0)->current_frame_host(), | 
| +                    "window.location.href = '" + blocked_url.spec() + "';")); | 
| + | 
| +  // The blocked frame should still fire a load event in its parent's process. | 
| +  EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle()); | 
| + | 
| +  // Check that the current RenderFrameHost has stopped loading. | 
| +  if (root->child_at(0)->current_frame_host()->is_loading()) { | 
| +    ADD_FAILURE() << "Blocked RenderFrameHost shouldn't be loading anything"; | 
| +    load_observer2.Wait(); | 
| +  } | 
| + | 
| +  // The blocked frame should stay at the old location. | 
| +  EXPECT_EQ(old_subframe_url, root->child_at(0)->current_url()); | 
| + | 
| +  // The blocked frame should keep the old title. | 
| +  std::string frame_title; | 
| +  EXPECT_TRUE(ExecuteScriptAndExtractString( | 
| +      root->child_at(0)->current_frame_host(), | 
| +      "domAutomationController.send(document.title)", &frame_title)); | 
| +  EXPECT_EQ("Title Of Awesomeness", frame_title); | 
| +} | 
| + | 
| +// Test that a cross-origin frame's navigation can be blocked by CSP frame-src. | 
| +// In this version of a test, CSP is inherited by srcdoc iframe from a parent | 
| +// that declared CSP via HTTP headers.  Cross-origin frame navigating to a | 
| +// blocked location is a child of the srcdoc iframe. | 
| +IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, | 
| +                       CrossSiteIframeBlockedByCSPInheritedBySrcDocParent) { | 
| +  GURL main_url( | 
| +      embedded_test_server()->GetURL("a.com", "/frame-src-self-and-b.html")); | 
| +  EXPECT_TRUE(NavigateToURL(shell(), main_url)); | 
| + | 
| +  FrameTreeNode* root = web_contents()->GetFrameTree()->root(); | 
| +  FrameTreeNode* srcdoc_frame = root->child_at(1); | 
| +  EXPECT_TRUE(srcdoc_frame != nullptr); | 
| +  FrameTreeNode* navigating_frame = srcdoc_frame->child_at(0); | 
| +  EXPECT_TRUE(navigating_frame != nullptr); | 
| + | 
| +  // Sanity-check that the test page has the expected shape for testing. | 
| +  // (the CSP should not have an effect on the already loaded frames). | 
| +  GURL old_subframe_url( | 
| +      embedded_test_server()->GetURL("b.com", "/title2.html")); | 
| +  EXPECT_TRUE(srcdoc_frame->HasSameOrigin(*root)); | 
| +  EXPECT_FALSE(srcdoc_frame->HasSameOrigin(*navigating_frame)); | 
| +  EXPECT_EQ(old_subframe_url, navigating_frame->current_url()); | 
| +  const std::vector<ContentSecurityPolicyHeader>& srcdoc_csp = | 
| +      srcdoc_frame->current_replication_state().accumulated_csp_headers; | 
| +  EXPECT_EQ(1u, srcdoc_csp.size()); | 
| +  EXPECT_EQ("frame-src 'self' http://b.com:*", srcdoc_csp[0].header_value); | 
| + | 
| +  // Monitor navigating_frame's load events via srcdoc_frame posting | 
| +  // a message to the parent frame. | 
| +  EXPECT_TRUE( | 
| +      ExecuteScript(root->current_frame_host(), | 
| +                    "window.addEventListener('message', function(event) {" | 
| +                    "  document.title = event.data;" | 
| +                    "});")); | 
| +  EXPECT_TRUE(ExecuteScript( | 
| +      srcdoc_frame->current_frame_host(), | 
| +      "document.querySelector('iframe').onload = " | 
| +      "    function() { window.top.postMessage('loaded', '*'); };")); | 
| +  EXPECT_TRUE( | 
| +      ExecuteScript(shell()->web_contents(), "document.title = 'not loaded';")); | 
| +  base::string16 expected_title(base::UTF8ToUTF16("loaded")); | 
| +  TitleWatcher title_watcher(shell()->web_contents(), expected_title); | 
| + | 
| +  // Try to navigate the subframe to a blocked URL. | 
| +  TestNavigationObserver load_observer2(shell()->web_contents()); | 
| +  GURL blocked_url = embedded_test_server()->GetURL("c.com", "/title3.html"); | 
| +  EXPECT_TRUE( | 
| +      ExecuteScript(navigating_frame->current_frame_host(), | 
| +                    "window.location.href = '" + blocked_url.spec() + "';")); | 
| + | 
| +  // The blocked frame should still fire a load event in its parent's process. | 
| +  EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle()); | 
| + | 
| +  // Check that the current RenderFrameHost has stopped loading. | 
| +  if (navigating_frame->current_frame_host()->is_loading()) { | 
| +    ADD_FAILURE() << "Blocked RenderFrameHost shouldn't be loading anything"; | 
| +    load_observer2.Wait(); | 
| +  } | 
| + | 
| +  // The blocked frame should stay at the old location. | 
| +  EXPECT_EQ(old_subframe_url, navigating_frame->current_url()); | 
| + | 
| +  // The blocked frame should keep the old title. | 
| +  std::string frame_title; | 
| +  EXPECT_TRUE(ExecuteScriptAndExtractString( | 
| +      navigating_frame->current_frame_host(), | 
| +      "domAutomationController.send(document.title)", &frame_title)); | 
| +  EXPECT_EQ("Title Of Awesomeness", frame_title); | 
| + | 
| +  // Navigate the subframe to a URL without CSP. | 
| +  NavigateFrameToURL(srcdoc_frame, | 
| +                     embedded_test_server()->GetURL("a.com", "/title1.html")); | 
| + | 
| +  // Verify that the frame's CSP got correctly reset to an empty set. | 
| +  EXPECT_EQ( | 
| +      0u, | 
| +      srcdoc_frame->current_replication_state().accumulated_csp_headers.size()); | 
| +} | 
| + | 
| IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, ScreenCoordinates) { | 
| GURL main_url(embedded_test_server()->GetURL( | 
| "a.com", "/cross_site_iframe_factory.html?a(b)")); | 
|  |