// Copyright 2020 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/base_switches.h" #include "base/files/file_path.h" #include "base/system/sys_info.h" #include "build/build_config.h" #include "components/prefs/pref_service.h" #include "components/site_isolation/features.h" #include "components/site_isolation/pref_names.h" #include "components/user_prefs/user_prefs.h" #include "content/public/browser/site_isolation_policy.h" #include "content/public/common/content_client.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/test_navigation_observer.h" #include "net/dns/mock_host_resolver.h" #include "testing/gmock/include/gmock/gmock.h" #include "weblayer/browser/browser_context_impl.h" #include "weblayer/browser/browser_impl.h" #include "weblayer/browser/content_browser_client_impl.h" #include "weblayer/browser/profile_impl.h" #include "weblayer/browser/tab_impl.h" #include "weblayer/shell/browser/shell.h" #include "weblayer/test/weblayer_browser_test.h" #include "weblayer/test/weblayer_browser_test_utils.h" namespace weblayer { using testing::IsEmpty; using testing::UnorderedElementsAre; class SiteIsolationBrowserTest : public WebLayerBrowserTest { public: SiteIsolationBrowserTest() { feature_list_.InitWithFeaturesAndParameters( {{site_isolation::features::kSitePerProcessOnlyForHighMemoryClients, {{site_isolation::features:: kSitePerProcessOnlyForHighMemoryClientsParamName, "128"}}}, {site_isolation::features::kSiteIsolationForPasswordSites, {}}}, {}); } std::vector GetSavedIsolatedSites() { PrefService* prefs = user_prefs::UserPrefs::Get(GetProfile()->GetBrowserContext()); auto* list = prefs->GetList(site_isolation::prefs::kUserTriggeredIsolatedOrigins); std::vector sites; for (const base::Value& value : list->GetList()) sites.push_back(value.GetString()); return sites; } // WebLayerBrowserTest: void SetUpOnMainThread() override { original_client_ = content::SetBrowserClientForTesting(&browser_client_); host_resolver()->AddRule("*", "127.0.0.1"); embedded_test_server()->ServeFilesFromSourceDirectory("weblayer/test/data"); ASSERT_TRUE(embedded_test_server()->Start()); ASSERT_FALSE( content::SiteIsolationPolicy::UseDedicatedProcessesForAllSites()); ASSERT_TRUE( content::SiteIsolationPolicy::AreDynamicIsolatedOriginsEnabled()); } void TearDownOnMainThread() override { content::SetBrowserClientForTesting(original_client_); } void SetUpCommandLine(base::CommandLine* command_line) override { command_line->AppendSwitch("ignore-certificate-errors"); // This way the test always sees the same amount of physical memory // (kLowMemoryDeviceThresholdMB = 512MB), regardless of how much memory is // available in the testing environment. base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableLowEndDeviceMode); EXPECT_EQ(512, base::SysInfo::AmountOfPhysicalMemoryMB()); } content::WebContents* GetWebContents() { return static_cast(shell()->tab())->web_contents(); } private: // A browser client which forces off strict site isolation, so the test can // assume password isolation is enabled. class SiteIsolationContentBrowserClient : public ContentBrowserClientImpl { public: SiteIsolationContentBrowserClient() : ContentBrowserClientImpl(nullptr) {} bool ShouldEnableStrictSiteIsolation() override { return false; } }; SiteIsolationContentBrowserClient browser_client_; content::ContentBrowserClient* original_client_ = nullptr; base::test::ScopedFeatureList feature_list_; }; IN_PROC_BROWSER_TEST_F(SiteIsolationBrowserTest, SiteIsIsolatedAfterEnteringPassword) { GURL url = embedded_test_server()->GetURL("sub.foo.com", "/simple_password_form.html"); NavigateAndWaitForCompletion(url, shell()); content::WebContents* contents = GetWebContents(); // foo.com should not be isolated to start with. Verify that a cross-site // iframe does not become an OOPIF. EXPECT_FALSE( contents->GetMainFrame()->GetSiteInstance()->RequiresDedicatedProcess()); std::string kAppendIframe = R"( var i = document.createElement('iframe'); i.id = 'child'; document.body.appendChild(i);)"; EXPECT_TRUE(content::ExecJs(contents, kAppendIframe)); GURL bar_url(embedded_test_server()->GetURL("bar.com", "/simple_page.html")); EXPECT_TRUE(NavigateIframeToURL(contents, "child", bar_url)); content::RenderFrameHost* child = ChildFrameAt(contents->GetMainFrame(), 0); EXPECT_FALSE(child->IsCrossProcessSubframe()); // Fill a form and submit through a button. content::TestNavigationObserver observer(contents); std::string kFillAndSubmit = "document.getElementById('username_field').value = 'temp';" "document.getElementById('password_field').value = 'random';" "document.getElementById('input_submit_button').click()"; EXPECT_TRUE(content::ExecJs(contents, kFillAndSubmit)); observer.Wait(); // Since there were no script references from other windows, we should've // swapped BrowsingInstances and put the result of the form submission into a // dedicated process, locked to foo.com. Check that a cross-site iframe now // becomes an OOPIF. EXPECT_TRUE( contents->GetMainFrame()->GetSiteInstance()->RequiresDedicatedProcess()); EXPECT_TRUE(ExecJs(contents, kAppendIframe)); EXPECT_TRUE(NavigateIframeToURL(contents, "child", bar_url)); child = ChildFrameAt(contents->GetMainFrame(), 0); EXPECT_TRUE(child->IsCrossProcessSubframe()); } // TODO(crbug.com/654704): Android does not support PRE_ tests. #if !defined(OS_ANDROID) IN_PROC_BROWSER_TEST_F(SiteIsolationBrowserTest, PRE_IsolatedSitesPersistAcrossRestarts) { // There shouldn't be any saved isolated origins to start with. EXPECT_THAT(GetSavedIsolatedSites(), IsEmpty()); // Isolate saved.com and saved2.com persistently. GURL saved_url = embedded_test_server()->GetURL("saved.com", "/simple_page.html"); content::SiteInstance::StartIsolatingSite(GetProfile()->GetBrowserContext(), saved_url); GURL saved2_url = embedded_test_server()->GetURL("saved2.com", "/simple_page.html"); content::SiteInstance::StartIsolatingSite(GetProfile()->GetBrowserContext(), saved2_url); NavigateAndWaitForCompletion(saved_url, shell()); EXPECT_TRUE(GetWebContents() ->GetMainFrame() ->GetSiteInstance() ->RequiresDedicatedProcess()); // Check that saved.com and saved2.com were saved to disk. EXPECT_THAT(GetSavedIsolatedSites(), UnorderedElementsAre("http://saved.com", "http://saved2.com")); } IN_PROC_BROWSER_TEST_F(SiteIsolationBrowserTest, IsolatedSitesPersistAcrossRestarts) { // Check that saved.com and saved2.com are still saved to disk. EXPECT_THAT(GetSavedIsolatedSites(), UnorderedElementsAre("http://saved.com", "http://saved2.com")); // Check that these sites utilize a dedicated process after restarting, but a // non-isolated foo.com URL does not. GURL saved_url = embedded_test_server()->GetURL("saved.com", "/simple_page.html"); GURL saved2_url = embedded_test_server()->GetURL("saved2.com", "/simple_page2.html"); GURL foo_url = embedded_test_server()->GetURL("foo.com", "/simple_page3.html"); NavigateAndWaitForCompletion(saved_url, shell()); content::WebContents* contents = GetWebContents(); EXPECT_TRUE( contents->GetMainFrame()->GetSiteInstance()->RequiresDedicatedProcess()); NavigateAndWaitForCompletion(saved2_url, shell()); EXPECT_TRUE( contents->GetMainFrame()->GetSiteInstance()->RequiresDedicatedProcess()); NavigateAndWaitForCompletion(foo_url, shell()); EXPECT_FALSE( contents->GetMainFrame()->GetSiteInstance()->RequiresDedicatedProcess()); } #endif IN_PROC_BROWSER_TEST_F(SiteIsolationBrowserTest, IsolatedSiteIsSavedOnlyOnce) { GURL saved_url = embedded_test_server()->GetURL("saved.com", "/simple_page.html"); content::SiteInstance::StartIsolatingSite(GetProfile()->GetBrowserContext(), saved_url); content::SiteInstance::StartIsolatingSite(GetProfile()->GetBrowserContext(), saved_url); content::SiteInstance::StartIsolatingSite(GetProfile()->GetBrowserContext(), saved_url); EXPECT_THAT(GetSavedIsolatedSites(), UnorderedElementsAre("http://saved.com")); } // Verify that serving a Clear-Site-Data header does not clear saved isolated // sites. Saved isolated sites should only be cleared by user-initiated actions. IN_PROC_BROWSER_TEST_F(SiteIsolationBrowserTest, ClearSiteDataHeaderDoesNotClearSavedIsolatedSites) { // Start an HTTPS server, as Clear-Site-Data is only available on HTTPS URLs. net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS); https_server.AddDefaultHandlers( base::FilePath(FILE_PATH_LITERAL("weblayer/test/data"))); ASSERT_TRUE(https_server.Start()); // Isolate saved.com and verify it's been saved to disk. GURL saved_url = https_server.GetURL("saved.com", "/clear_site_data.html"); content::SiteInstance::StartIsolatingSite(GetProfile()->GetBrowserContext(), saved_url); EXPECT_THAT(GetSavedIsolatedSites(), UnorderedElementsAre("https://saved.com")); // Navigate to a URL that serves a Clear-Site-Data header for cache, cookies, // and DOM storage. This is the most that a Clear-Site-Data header could // clear, and this should not clear saved isolated sites. NavigateAndWaitForCompletion(saved_url, shell()); EXPECT_THAT(GetSavedIsolatedSites(), UnorderedElementsAre("https://saved.com")); } IN_PROC_BROWSER_TEST_F(SiteIsolationBrowserTest, ExplicitClearBrowsingDataClearsSavedIsolatedSites) { GURL saved_url = embedded_test_server()->GetURL("saved.com", "/simple_page.html"); content::SiteInstance::StartIsolatingSite(GetProfile()->GetBrowserContext(), saved_url); EXPECT_THAT(GetSavedIsolatedSites(), UnorderedElementsAre("http://saved.com")); base::RunLoop run_loop; base::Time now = base::Time::Now(); GetProfile()->ClearBrowsingData({BrowsingDataType::COOKIES_AND_SITE_DATA}, now - base::TimeDelta::FromDays(1), now, run_loop.QuitClosure()); run_loop.Run(); EXPECT_THAT(GetSavedIsolatedSites(), IsEmpty()); } } // namespace weblayer