// Copyright (c) 2012 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 "content/browser/site_instance_impl.h" #include <stddef.h> #include <memory> #include <string> #include <vector> #include "base/command_line.h" #include "base/compiler_specific.h" #include "base/memory/ptr_util.h" #include "base/memory/raw_ptr.h" #include "base/run_loop.h" #include "base/test/mock_log.h" #include "base/test/scoped_command_line.h" #include "base/test/scoped_feature_list.h" #include "content/browser/browsing_instance.h" #include "content/browser/child_process_security_policy_impl.h" #include "content/browser/isolated_origin_util.h" #include "content/browser/process_lock.h" #include "content/browser/renderer_host/navigation_entry_impl.h" #include "content/browser/renderer_host/render_process_host_impl.h" #include "content/browser/renderer_host/render_view_host_impl.h" #include "content/browser/site_info.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/browser/webui/content_web_ui_controller_factory.h" #include "content/browser/webui/web_ui_controller_factory_registry.h" #include "content/public/browser/browser_or_resource_context.h" #include "content/public/browser/site_isolation_policy.h" #include "content/public/browser/web_ui_controller.h" #include "content/public/common/bindings_policy.h" #include "content/public/common/content_client.h" #include "content/public/common/content_constants.h" #include "content/public/common/content_features.h" #include "content/public/common/content_switches.h" #include "content/public/common/url_constants.h" #include "content/public/common/url_utils.h" #include "content/public/test/browser_task_environment.h" #include "content/public/test/mock_render_process_host.h" #include "content/public/test/scoped_web_ui_controller_factory_registration.h" #include "content/public/test/test_browser_context.h" #include "content/public/test/test_utils.h" #include "content/test/storage_partition_test_helpers.h" #include "content/test/test_content_browser_client.h" #include "content/test/test_content_client.h" #include "content/test/test_render_view_host.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/origin.h" #include "url/url_util.h" namespace content { namespace { using IsolatedOriginSource = ChildProcessSecurityPolicy::IsolatedOriginSource; bool DoesURLRequireDedicatedProcess(const IsolationContext& isolation_context, const GURL& url) { return SiteInfo::CreateForTesting(isolation_context, url) .RequiresDedicatedProcess(isolation_context); } SiteInfo CreateSimpleSiteInfo(const GURL& process_lock_url, bool requires_origin_keyed_process) { return SiteInfo( GURL("https://www.foo.com"), process_lock_url, requires_origin_keyed_process, CreateStoragePartitionConfigForTesting(), WebExposedIsolationInfo::CreateNonIsolated(), false /* is_guest */, false /* does_site_request_dedicated_process_for_coop */, false /* is_jit_disabled */, false /* is_pdf */); } } // namespace const char kPrivilegedScheme[] = "privileged"; const char kCustomStandardScheme[] = "custom-standard"; class SiteInstanceTestBrowserClient : public TestContentBrowserClient { public: bool IsSuitableHost(RenderProcessHost* process_host, const GURL& site_url) override { return (privileged_process_id_ == process_host->GetID()) == site_url.SchemeIs(kPrivilegedScheme); } void set_privileged_process_id(int process_id) { privileged_process_id_ = process_id; } void SiteInstanceDeleting(content::SiteInstance* site_instance) override { site_instance_delete_count_++; // Infer deletion of the browsing instance. if (static_cast<SiteInstanceImpl*>(site_instance) ->browsing_instance_->HasOneRef()) { browsing_instance_delete_count_++; } } int GetAndClearSiteInstanceDeleteCount() { int result = site_instance_delete_count_; site_instance_delete_count_ = 0; return result; } int GetAndClearBrowsingInstanceDeleteCount() { int result = browsing_instance_delete_count_; browsing_instance_delete_count_ = 0; return result; } private: int privileged_process_id_ = -1; int site_instance_delete_count_ = 0; int browsing_instance_delete_count_ = 0; ScopedWebUIControllerFactoryRegistration factory_registration_{ ContentWebUIControllerFactory::GetInstance()}; }; class SiteInstanceTest : public testing::Test { public: SiteInstanceTest() : old_browser_client_(nullptr) { url::AddStandardScheme(kPrivilegedScheme, url::SCHEME_WITH_HOST); url::AddStandardScheme(kCustomStandardScheme, url::SCHEME_WITH_HOST); } GURL GetSiteForURL(const IsolationContext& isolation_context, const GURL& url) { return SiteInfo::Create(isolation_context, UrlInfo(UrlInfoInit(url))) .site_url(); } void SetUp() override { old_browser_client_ = SetBrowserClientForTesting(&browser_client_); RenderProcessHostImpl::set_render_process_host_factory_for_testing( &rph_factory_); SiteIsolationPolicy::DisableFlagCachingForTesting(); } void TearDown() override { // Ensure that no RenderProcessHosts are left over after the tests. EXPECT_TRUE(RenderProcessHost::AllHostsIterator().IsAtEnd()); SetBrowserClientForTesting(old_browser_client_); RenderProcessHostImpl::set_render_process_host_factory_for_testing(nullptr); } void set_privileged_process_id(int process_id) { browser_client_.set_privileged_process_id(process_id); } void DrainMessageLoop() { // We don't just do this in TearDown() because we create TestBrowserContext // objects in each test, which will be destructed before // TearDown() is called. base::RunLoop().RunUntilIdle(); } SiteInstanceTestBrowserClient* browser_client() { return &browser_client_; } bool IsIsolatedOrigin(const GURL& url) { // It's fine to use an IsolationContext without an associated // BrowsingInstance, since this helper is used by tests that deal with // globally isolated origins. IsolationContext isolation_context(&context_); auto* policy = ChildProcessSecurityPolicyImpl::GetInstance(); return policy->IsIsolatedOrigin(isolation_context, url::Origin::Create(url), false /* origin_requests_isolation */); } BrowserContext* context() { return &context_; } GURL GetSiteForURL(const GURL& url) { return GetSiteInfoForURL(url).site_url(); } SiteInfo GetSiteInfoForURL(const std::string& url) { return SiteInfo::CreateForTesting(IsolationContext(&context_), GURL(url)); } SiteInfo GetSiteInfoForURL(const GURL& url) { return SiteInfo::CreateForTesting(IsolationContext(&context_), url); } static bool IsSameSite(BrowserContext* context, const GURL& url1, const GURL& url2) { return SiteInstanceImpl::IsSameSite(IsolationContext(context), UrlInfo(UrlInfoInit(url1)), UrlInfo(UrlInfoInit(url2)), /*should_compare_effective_urls=*/true); } private: BrowserTaskEnvironment task_environment_; TestBrowserContext context_; SiteInstanceTestBrowserClient browser_client_; raw_ptr<ContentBrowserClient> old_browser_client_; MockRenderProcessHostFactory rph_factory_; url::ScopedSchemeRegistryForTests scoped_registry_; }; // Tests that SiteInfo works correct as a key for std::map and std::set. // Test SiteInfos with identical site URLs but various lock URLs, including // variations of each that are origin keyed ("ok"). TEST_F(SiteInstanceTest, SiteInfoAsContainerKey) { auto site_info_1 = CreateSimpleSiteInfo( GURL("https://foo.com"), false /* requires_origin_keyed_process */); auto site_info_1ok = CreateSimpleSiteInfo( GURL("https://foo.com"), true /* requires_origin_keyed_process */); auto site_info_2 = CreateSimpleSiteInfo( GURL("https://www.foo.com"), false /* requires_origin_keyed_process */); auto site_info_2ok = CreateSimpleSiteInfo( GURL("https://www.foo.com"), true /* requires_origin_keyed_process */); auto site_info_3 = CreateSimpleSiteInfo( GURL("https://sub.foo.com"), false /* requires_origin_keyed_process */); auto site_info_3ok = CreateSimpleSiteInfo( GURL("https://sub.foo.com"), true /* requires_origin_keyed_process */); auto site_info_4 = CreateSimpleSiteInfo(GURL(), false /* requires_origin_keyed_process */); auto site_info_4ok = CreateSimpleSiteInfo(GURL(), true /* requires_origin_keyed_process */); // Test IsSamePrincipalWith. EXPECT_TRUE(site_info_1.IsSamePrincipalWith(site_info_1)); EXPECT_FALSE(site_info_1.IsSamePrincipalWith(site_info_1ok)); EXPECT_FALSE(site_info_1.IsSamePrincipalWith(site_info_2)); EXPECT_FALSE(site_info_1.IsSamePrincipalWith(site_info_3)); EXPECT_FALSE(site_info_1.IsSamePrincipalWith(site_info_4)); EXPECT_TRUE(site_info_2.IsSamePrincipalWith(site_info_2)); EXPECT_FALSE(site_info_2.IsSamePrincipalWith(site_info_2ok)); EXPECT_FALSE(site_info_2.IsSamePrincipalWith(site_info_3)); EXPECT_FALSE(site_info_2.IsSamePrincipalWith(site_info_4)); EXPECT_TRUE(site_info_3.IsSamePrincipalWith(site_info_3)); EXPECT_FALSE(site_info_3.IsSamePrincipalWith(site_info_3ok)); EXPECT_FALSE(site_info_3.IsSamePrincipalWith(site_info_4)); EXPECT_TRUE(site_info_4.IsSamePrincipalWith(site_info_4)); EXPECT_FALSE(site_info_4.IsSamePrincipalWith(site_info_4ok)); // Test SiteInfoOperators. EXPECT_EQ(site_info_1, site_info_1); EXPECT_NE(site_info_1, site_info_2); EXPECT_NE(site_info_1, site_info_3); EXPECT_NE(site_info_1, site_info_4); EXPECT_EQ(site_info_2, site_info_2); EXPECT_NE(site_info_2, site_info_3); EXPECT_NE(site_info_2, site_info_4); EXPECT_EQ(site_info_3, site_info_3); EXPECT_NE(site_info_3, site_info_4); EXPECT_EQ(site_info_4, site_info_4); EXPECT_TRUE(site_info_1 < site_info_3); // 'f' before 's'/ EXPECT_TRUE(site_info_3 < site_info_2); // 's' before 'w'/ EXPECT_TRUE(site_info_4 < site_info_1); // Empty string first. // Check that SiteInfos with differing values of // `does_site_request_dedicated_process_for_coop_` are still considered // same-principal. auto site_info_1_with_isolation_request = SiteInfo( GURL("https://www.foo.com") /* site_url */, GURL("https://foo.com") /* process_lock_url */, false /* requires_origin_keyed_process */, CreateStoragePartitionConfigForTesting(), WebExposedIsolationInfo::CreateNonIsolated(), false /* is_guest */, true /* does_site_request_dedicated_process_for_coop */, false /* is_jit_disabled */, false /* is_pdf */); EXPECT_TRUE( site_info_1.IsSamePrincipalWith(site_info_1_with_isolation_request)); EXPECT_EQ(site_info_1, site_info_1_with_isolation_request); // Check that SiteInfos with differing values of `is_jit_disabled` are not // considered same-principal. auto site_info_1_with_jit_disabled = SiteInfo( GURL("https://www.foo.com") /* site_url */, GURL("https://foo.com") /* process_lock_url */, false /* requires_origin_keyed_process */, CreateStoragePartitionConfigForTesting(), WebExposedIsolationInfo::CreateNonIsolated(), false /* is_guest */, false /* does_site_request_dedicated_process_for_coop */, true /* is_jit_disabled */, false /* is_pdf */); EXPECT_FALSE(site_info_1.IsSamePrincipalWith(site_info_1_with_jit_disabled)); // Check that SiteInfos with differing values of `is_pdf` are not considered // same-principal. auto site_info_1_with_pdf = SiteInfo( GURL("https://www.foo.com") /* site_url */, GURL("https://foo.com") /* process_lock_url */, false /* requires_origin_keyed_process */, CreateStoragePartitionConfigForTesting(), WebExposedIsolationInfo::CreateNonIsolated(), false /* is_guest */, false /* does_site_request_dedicated_process_for_coop */, false /* is_jit_disabled */, true /* is_pdf */); EXPECT_FALSE(site_info_1.IsSamePrincipalWith(site_info_1_with_pdf)); { std::map<SiteInfo, int> test_map; // Map tests: different lock URLs. test_map[site_info_1] = 1; test_map[site_info_2] = 2; test_map[site_info_4] = 4; // Make sure std::map treated the different SiteInfo's as distinct. EXPECT_EQ(3u, test_map.size()); // Test that std::map::find() looks up the correct key. auto it1 = test_map.find(site_info_1); EXPECT_NE(it1, test_map.end()); EXPECT_EQ(1, it1->second); auto it2 = test_map.find(site_info_2); EXPECT_NE(it2, test_map.end()); EXPECT_EQ(2, it2->second); EXPECT_EQ(test_map.end(), test_map.find(site_info_3)); auto it4 = test_map.find(site_info_4); EXPECT_NE(it4, test_map.end()); EXPECT_EQ(4, it4->second); // Check that `site_info_1` and `site_info_1_with_isolation_request` // collapse into the same key. test_map[site_info_1_with_isolation_request] = 5; EXPECT_EQ(3u, test_map.size()); it1 = test_map.find(site_info_1); EXPECT_NE(it1, test_map.end()); EXPECT_EQ(5, it1->second); } { std::map<SiteInfo, int> test_map; // Map tests: different lock URLs and origin keys. test_map[site_info_1] = 1; test_map[site_info_2] = 2; test_map[site_info_4] = 4; test_map[site_info_1ok] = 11; test_map[site_info_2ok] = 12; test_map[site_info_4ok] = 14; // Make sure std::map treated the different SiteInfo's as distinct. EXPECT_EQ(6u, test_map.size()); // Test that std::map::find() looks up the correct key with // requires_origin_keyed_process == true. auto it1 = test_map.find(site_info_1ok); EXPECT_NE(it1, test_map.end()); EXPECT_EQ(11, it1->second); auto it2 = test_map.find(site_info_2ok); EXPECT_NE(it2, test_map.end()); EXPECT_EQ(12, it2->second); EXPECT_EQ(test_map.end(), test_map.find(site_info_3)); EXPECT_EQ(test_map.end(), test_map.find(site_info_3ok)); auto it4 = test_map.find(site_info_4ok); EXPECT_NE(it4, test_map.end()); EXPECT_EQ(14, it4->second); } { std::set<SiteInfo> test_set; // Set tests. test_set.insert(site_info_1); test_set.insert(site_info_2); test_set.insert(site_info_4); EXPECT_EQ(3u, test_set.size()); auto itS1 = test_set.find(site_info_1); auto itS2 = test_set.find(site_info_2); auto itS3 = test_set.find(site_info_3); auto itS4 = test_set.find(site_info_4); EXPECT_NE(test_set.end(), itS1); EXPECT_NE(test_set.end(), itS2); EXPECT_EQ(test_set.end(), itS3); EXPECT_NE(test_set.end(), itS4); EXPECT_EQ(site_info_1, *itS1); EXPECT_EQ(site_info_2, *itS2); EXPECT_EQ(site_info_4, *itS4); } { std::set<SiteInfo> test_set; // Set tests, testing requires_origin_keyed_process. test_set.insert(site_info_1); test_set.insert(site_info_2); test_set.insert(site_info_4); test_set.insert(site_info_1ok); test_set.insert(site_info_2ok); test_set.insert(site_info_4ok); EXPECT_EQ(6u, test_set.size()); auto itS1 = test_set.find(site_info_1ok); auto itS2 = test_set.find(site_info_2ok); auto itS3 = test_set.find(site_info_3ok); auto itS4 = test_set.find(site_info_4ok); EXPECT_NE(test_set.end(), itS1); EXPECT_NE(test_set.end(), itS2); EXPECT_EQ(test_set.end(), itS3); EXPECT_NE(test_set.end(), itS4); EXPECT_EQ(site_info_1ok, *itS1); EXPECT_EQ(site_info_2ok, *itS2); EXPECT_EQ(site_info_4ok, *itS4); } } // Test to ensure no memory leaks for SiteInstance objects. TEST_F(SiteInstanceTest, SiteInstanceDestructor) { TestBrowserContext context; // The existence of this object will cause WebContentsImpl to create our // test one instead of the real one. RenderViewHostTestEnabler rvh_test_enabler; const GURL url("test:foo"); // Ensure that instances are deleted when their NavigationEntries are gone. scoped_refptr<SiteInstanceImpl> instance = SiteInstanceImpl::Create(&context); EXPECT_EQ(0, browser_client()->GetAndClearSiteInstanceDeleteCount()); NavigationEntryImpl* e1 = new NavigationEntryImpl( instance, url, Referrer(), absl::nullopt, std::u16string(), ui::PAGE_TRANSITION_LINK, false, nullptr /* blob_url_loader_factory */, false /* is_initial_entry */); // Redundantly setting e1's SiteInstance shouldn't affect the ref count. e1->set_site_instance(instance); EXPECT_EQ(0, browser_client()->GetAndClearSiteInstanceDeleteCount()); EXPECT_EQ(0, browser_client()->GetAndClearBrowsingInstanceDeleteCount()); // Add a second reference NavigationEntryImpl* e2 = new NavigationEntryImpl( instance, url, Referrer(), absl::nullopt, std::u16string(), ui::PAGE_TRANSITION_LINK, false, nullptr /* blob_url_loader_factory */, false /* is_initial_entry */); instance = nullptr; EXPECT_EQ(0, browser_client()->GetAndClearSiteInstanceDeleteCount()); EXPECT_EQ(0, browser_client()->GetAndClearBrowsingInstanceDeleteCount()); // Now delete both entries and be sure the SiteInstance goes away. delete e1; EXPECT_EQ(0, browser_client()->GetAndClearSiteInstanceDeleteCount()); EXPECT_EQ(0, browser_client()->GetAndClearBrowsingInstanceDeleteCount()); delete e2; // instance is now deleted EXPECT_EQ(1, browser_client()->GetAndClearSiteInstanceDeleteCount()); EXPECT_EQ(1, browser_client()->GetAndClearBrowsingInstanceDeleteCount()); // browsing_instance is now deleted // Ensure that instances are deleted when their RenderViewHosts are gone. std::unique_ptr<TestBrowserContext> browser_context(new TestBrowserContext()); { std::unique_ptr<WebContents> web_contents( WebContents::Create(WebContents::CreateParams( browser_context.get(), SiteInstance::Create(browser_context.get())))); EXPECT_EQ(0, browser_client()->GetAndClearSiteInstanceDeleteCount()); EXPECT_EQ(0, browser_client()->GetAndClearBrowsingInstanceDeleteCount()); } // Make sure that we flush any messages related to the above WebContentsImpl // destruction. DrainMessageLoop(); EXPECT_EQ(1, browser_client()->GetAndClearSiteInstanceDeleteCount()); EXPECT_EQ(1, browser_client()->GetAndClearBrowsingInstanceDeleteCount()); // contents is now deleted, along with instance and browsing_instance } // Verifies some basic properties of default SiteInstances. TEST_F(SiteInstanceTest, DefaultSiteInstanceProperties) { TestBrowserContext browser_context; // Make sure feature list command-line options are set in a way that forces // default SiteInstance creation on all platforms. base::test::ScopedFeatureList feature_list; feature_list.InitWithFeatures( /* enable */ {features::kProcessSharingWithDefaultSiteInstances}, /* disable */ {features::kProcessSharingWithStrictSiteInstances}); EXPECT_TRUE(base::FeatureList::IsEnabled( features::kProcessSharingWithDefaultSiteInstances)); EXPECT_FALSE(base::FeatureList::IsEnabled( features::kProcessSharingWithStrictSiteInstances)); base::test::ScopedCommandLine scoped_command_line; // Disable site isolation so we can get default SiteInstances on all // platforms. scoped_command_line.GetProcessCommandLine()->AppendSwitch( switches::kDisableSiteIsolation); // If --site-per-process was manually appended, remove it; this interferes // with default SiteInstances. scoped_command_line.GetProcessCommandLine()->RemoveSwitch( switches::kSitePerProcess); auto site_instance = SiteInstanceImpl::CreateForUrlInfo( &browser_context, UrlInfo::CreateForTesting(GURL("http://foo.com"))); EXPECT_TRUE(site_instance->IsDefaultSiteInstance()); EXPECT_TRUE(site_instance->HasSite()); EXPECT_EQ(site_instance->GetSiteInfo(), SiteInfo::CreateForDefaultSiteInstance( &browser_context, StoragePartitionConfig::CreateDefault(&browser_context), WebExposedIsolationInfo::CreateNonIsolated())); EXPECT_FALSE(site_instance->RequiresDedicatedProcess()); } // Ensure that default SiteInstances are deleted when all references to them // are gone. TEST_F(SiteInstanceTest, DefaultSiteInstanceDestruction) { TestBrowserContext browser_context; base::test::ScopedCommandLine scoped_command_line; // Disable site isolation so we can get default SiteInstances on all // platforms. scoped_command_line.GetProcessCommandLine()->AppendSwitch( switches::kDisableSiteIsolation); // Ensure that default SiteInstances are deleted when all references to them // are gone. auto site_instance = SiteInstanceImpl::CreateForTesting( &browser_context, GURL("http://foo.com")); EXPECT_EQ(AreDefaultSiteInstancesEnabled(), site_instance->IsDefaultSiteInstance()); site_instance.reset(); EXPECT_EQ(1, browser_client()->GetAndClearSiteInstanceDeleteCount()); EXPECT_EQ(1, browser_client()->GetAndClearBrowsingInstanceDeleteCount()); } // Test to ensure GetProcess returns and creates processes correctly. TEST_F(SiteInstanceTest, GetProcess) { // Ensure that GetProcess returns a process. std::unique_ptr<TestBrowserContext> browser_context(new TestBrowserContext()); scoped_refptr<SiteInstanceImpl> instance( SiteInstanceImpl::Create(browser_context.get())); RenderProcessHost* host1 = instance->GetProcess(); EXPECT_TRUE(host1 != nullptr); // Ensure that GetProcess creates a new process. scoped_refptr<SiteInstanceImpl> instance2( SiteInstanceImpl::Create(browser_context.get())); RenderProcessHost* host2 = instance2->GetProcess(); EXPECT_TRUE(host2 != nullptr); EXPECT_NE(host1, host2); DrainMessageLoop(); } // Test to ensure SetSite and site() work properly. TEST_F(SiteInstanceTest, SetSite) { TestBrowserContext context; scoped_refptr<SiteInstanceImpl> instance(SiteInstanceImpl::Create(&context)); EXPECT_FALSE(instance->HasSite()); EXPECT_TRUE(instance->GetSiteURL().is_empty()); instance->SetSite( UrlInfo::CreateForTesting(GURL("http://www.google.com/index.html"))); EXPECT_EQ(GURL("http://google.com"), instance->GetSiteURL()); EXPECT_TRUE(instance->HasSite()); DrainMessageLoop(); } // Test to ensure GetSiteForURL properly returns sites for URLs. TEST_F(SiteInstanceTest, GetSiteForURL) { TestBrowserContext context; // Pages are irrelevant. GURL test_url = GURL("http://www.google.com/index.html"); GURL site_url = GetSiteForURL(test_url); EXPECT_EQ(GURL("http://google.com"), site_url); EXPECT_EQ("http", site_url.scheme()); EXPECT_EQ("google.com", site_url.host()); // Ports are irrelevant. test_url = GURL("https://www.google.com:8080"); site_url = GetSiteForURL(test_url); EXPECT_EQ(GURL("https://google.com"), site_url); // Punycode is canonicalized. test_url = GURL("http://☃snowperson☃.net:333/"); site_url = GetSiteForURL(test_url); EXPECT_EQ(GURL("http://xn--snowperson-di0gka.net"), site_url); // Username and password are stripped out. test_url = GURL("ftp://username:password@ftp.chromium.org/files/README"); site_url = GetSiteForURL(test_url); EXPECT_EQ(GURL("ftp://chromium.org"), site_url); // Literal IP addresses of any flavor are okay. test_url = GURL("http://127.0.0.1/a.html"); site_url = GetSiteForURL(test_url); EXPECT_EQ(GURL("http://127.0.0.1"), site_url); EXPECT_EQ("127.0.0.1", site_url.host()); test_url = GURL("http://2130706433/a.html"); site_url = GetSiteForURL(test_url); EXPECT_EQ(GURL("http://127.0.0.1"), site_url); EXPECT_EQ("127.0.0.1", site_url.host()); test_url = GURL("http://[::1]:2/page.html"); site_url = GetSiteForURL(test_url); EXPECT_EQ(GURL("http://[::1]"), site_url); EXPECT_EQ("[::1]", site_url.host()); // Hostnames without TLDs are okay. test_url = GURL("http://foo/a.html"); site_url = GetSiteForURL(test_url); EXPECT_EQ(GURL("http://foo"), site_url); EXPECT_EQ("foo", site_url.host()); // File URLs should include the scheme. test_url = GURL("file:///C:/Downloads/"); site_url = GetSiteForURL(test_url); EXPECT_EQ(GURL("file:"), site_url); EXPECT_EQ("file", site_url.scheme()); EXPECT_FALSE(site_url.has_host()); // Some file URLs have hosts in the path. For consistency with Blink (which // maps *all* file://... URLs into "file://" origin) such file URLs still need // to map into "file:" site URL. See also https://crbug.com/776160. test_url = GURL("file://server/path"); site_url = GetSiteForURL(test_url); EXPECT_EQ(GURL("file:"), site_url); EXPECT_EQ("file", site_url.scheme()); EXPECT_FALSE(site_url.has_host()); // Data URLs should include the whole URL, except for the hash. test_url = GURL("data:text/html,foo"); site_url = GetSiteForURL(test_url); EXPECT_EQ(test_url, site_url); EXPECT_EQ("data", site_url.scheme()); EXPECT_FALSE(site_url.has_host()); test_url = GURL("data:text/html,foo#bar"); site_url = GetSiteForURL(test_url); EXPECT_FALSE(site_url.has_ref()); EXPECT_NE(test_url, site_url); EXPECT_TRUE(site_url.EqualsIgnoringRef(test_url)); // Javascript URLs should include the scheme. test_url = GURL("javascript:foo();"); site_url = GetSiteForURL(test_url); EXPECT_EQ(GURL("javascript:"), site_url); EXPECT_EQ("javascript", site_url.scheme()); EXPECT_FALSE(site_url.has_host()); // Blob URLs extract the site from the origin. test_url = GURL( "blob:https://www.ftp.chromium.org/" "4d4ff040-6d61-4446-86d3-13ca07ec9ab9"); site_url = GetSiteForURL(test_url); EXPECT_EQ(GURL("https://chromium.org"), site_url); // Blob URLs with file origin also extract the site from the origin. test_url = GURL("blob:file:///1029e5a4-2983-4b90-a585-ed217563acfeb"); site_url = GetSiteForURL(test_url); EXPECT_EQ(GURL("file:"), site_url); EXPECT_EQ("file", site_url.scheme()); EXPECT_FALSE(site_url.has_host()); // Blob URLs created from a unique origin use the full URL as the site URL, // except for the hash. test_url = GURL("blob:null/1029e5a4-2983-4b90-a585-ed217563acfeb"); site_url = GetSiteForURL(test_url); EXPECT_EQ(test_url, site_url); test_url = GURL("blob:null/1029e5a4-2983-4b90-a585-ed217563acfeb#foo"); site_url = GetSiteForURL(test_url); EXPECT_FALSE(site_url.has_ref()); EXPECT_NE(test_url, site_url); EXPECT_TRUE(site_url.EqualsIgnoringRef(test_url)); // Private domains are preserved, appspot being such a site. test_url = GURL( "blob:http://www.example.appspot.com:44/" "4d4ff040-6d61-4446-86d3-13ca07ec9ab9"); site_url = GetSiteForURL(test_url); EXPECT_EQ(GURL("http://example.appspot.com"), site_url); // The site of filesystem URLs is determined by the inner URL. test_url = GURL("filesystem:http://www.google.com/foo/bar.html?foo#bar"); site_url = GetSiteForURL(test_url); EXPECT_EQ(GURL("http://google.com"), site_url); // Error page URLs. auto error_site_info = SiteInfo::CreateForErrorPage(CreateStoragePartitionConfigForTesting()); test_url = GURL(kUnreachableWebDataURL); site_url = GetSiteForURL(test_url); EXPECT_EQ(error_site_info.site_url(), site_url); // Verify that other URLs that use the chrome-error scheme also map // to the error page SiteInfo. These type of URLs should not appear in the // codebase, but the mapping is intended to cover the whole scheme. test_url = GURL("chrome-error://someerror"); site_url = GetSiteForURL(test_url); EXPECT_EQ(error_site_info.site_url(), site_url); DrainMessageLoop(); } // Test that process lock URLs are computed without using effective URLs. TEST_F(SiteInstanceTest, ProcessLockDoesNotUseEffectiveURL) { GURL test_url("https://some.app.foo.com/"); GURL nonapp_site_url("https://foo.com/"); GURL app_url("https://app.com/"); EffectiveURLContentBrowserClient modified_client( test_url, app_url, /* requires_dedicated_process */ true); ContentBrowserClient* regular_client = SetBrowserClientForTesting(&modified_client); std::unique_ptr<TestBrowserContext> browser_context(new TestBrowserContext()); IsolationContext isolation_context(browser_context.get()); // Sanity check that SiteInfo fields influenced by effective URLs are set // properly. The site URL should correspond to the effective URL's site // (app.com), and the process lock URL should refer to the original URL's site // (foo.com). { auto site_info = SiteInfo::CreateForTesting(isolation_context, test_url); EXPECT_EQ(nonapp_site_url, site_info.process_lock_url()); EXPECT_EQ(app_url, site_info.site_url()); } SiteInfo expected_site_info( app_url /* site_url */, nonapp_site_url /* process_lock_url */, false /* requires_origin_keyed_process */, CreateStoragePartitionConfigForTesting(), WebExposedIsolationInfo::CreateNonIsolated(), false /* is_guest */, false /* does_site_request_dedicated_process_for_coop */, false /* is_jit_disabled */, false /* is_pdf */); // New SiteInstance in a new BrowsingInstance with a predetermined URL. { scoped_refptr<SiteInstanceImpl> site_instance = SiteInstanceImpl::CreateForTesting(browser_context.get(), test_url); EXPECT_EQ(expected_site_info, site_instance->GetSiteInfo()); } // New related SiteInstance from an existing SiteInstance with a // predetermined URL. { scoped_refptr<SiteInstanceImpl> bar_site_instance = SiteInstanceImpl::CreateForTesting(browser_context.get(), GURL("https://bar.com/")); scoped_refptr<SiteInstance> site_instance = bar_site_instance->GetRelatedSiteInstance(test_url); auto* site_instance_impl = static_cast<SiteInstanceImpl*>(site_instance.get()); EXPECT_EQ(expected_site_info, site_instance_impl->GetSiteInfo()); } // New SiteInstance with a lazily assigned site URL. { scoped_refptr<SiteInstanceImpl> site_instance = SiteInstanceImpl::Create(browser_context.get()); EXPECT_FALSE(site_instance->HasSite()); site_instance->SetSite(UrlInfo::CreateForTesting(test_url)); EXPECT_EQ(expected_site_info, site_instance->GetSiteInfo()); } SetBrowserClientForTesting(regular_client); } // Test of distinguishing URLs from different sites. Most of this logic is // tested in RegistryControlledDomainTest. This test focuses on URLs with // different schemes or ports. TEST_F(SiteInstanceTest, IsSameSite) { TestBrowserContext context; GURL url_foo = GURL("http://foo/a.html"); GURL url_foo2 = GURL("http://foo/b.html"); GURL url_foo_https = GURL("https://foo/a.html"); GURL url_foo_port = GURL("http://foo:8080/a.html"); GURL url_javascript = GURL("javascript:alert(1);"); GURL url_blank = GURL(url::kAboutBlankURL); // Same scheme and port -> same site. EXPECT_TRUE(IsSameSite(&context, url_foo, url_foo2)); // Different scheme -> different site. EXPECT_FALSE(IsSameSite(&context, url_foo, url_foo_https)); // Different port -> same site. // (Changes to document.domain make renderer ignore the port.) EXPECT_TRUE(IsSameSite(&context, url_foo, url_foo_port)); // JavaScript links should be considered same site for anything. EXPECT_TRUE(IsSameSite(&context, url_javascript, url_foo)); EXPECT_TRUE(IsSameSite(&context, url_javascript, url_foo_https)); EXPECT_TRUE(IsSameSite(&context, url_javascript, url_foo_port)); // Navigating to a blank page is considered the same site. EXPECT_TRUE(IsSameSite(&context, url_foo, url_blank)); EXPECT_TRUE(IsSameSite(&context, url_foo_https, url_blank)); EXPECT_TRUE(IsSameSite(&context, url_foo_port, url_blank)); // Navigating from a blank site is not considered to be the same site. EXPECT_FALSE(IsSameSite(&context, url_blank, url_foo)); EXPECT_FALSE(IsSameSite(&context, url_blank, url_foo_https)); EXPECT_FALSE(IsSameSite(&context, url_blank, url_foo_port)); DrainMessageLoop(); } // Test that two file URLs are considered same-site if they have the same path, // even if they have different fragments. TEST_F(SiteInstanceTest, IsSameSiteForFileURLs) { TestBrowserContext context; // Two identical file URLs should be same-site. EXPECT_TRUE(IsSameSite(&context, GURL("file:///foo/bar.html"), GURL("file:///foo/bar.html"))); // File URLs with the same path but different fragment are considered // same-site. EXPECT_TRUE(IsSameSite(&context, GURL("file:///foo/bar.html"), GURL("file:///foo/bar.html#baz"))); EXPECT_TRUE(IsSameSite(&context, GURL("file:///foo/bar.html#baz"), GURL("file:///foo/bar.html"))); EXPECT_TRUE(IsSameSite(&context, GURL("file:///foo/bar.html#baz"), GURL("file:///foo/bar.html#qux"))); EXPECT_TRUE(IsSameSite(&context, GURL("file:///#abc"), GURL("file:///#def"))); // Other cases are cross-site. EXPECT_FALSE(IsSameSite(&context, GURL("file:///foo.html"), GURL("file:///foo/bar.html"))); EXPECT_FALSE( IsSameSite(&context, GURL("file:///#bar"), GURL("file:///foo/#bar"))); } // Test to ensure that there is only one SiteInstance per site in a given // BrowsingInstance, when process-per-site is not in use. TEST_F(SiteInstanceTest, OneSiteInstancePerSite) { ASSERT_FALSE(base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kProcessPerSite)); std::unique_ptr<TestBrowserContext> browser_context(new TestBrowserContext()); BrowsingInstance* browsing_instance = new BrowsingInstance( browser_context.get(), WebExposedIsolationInfo::CreateNonIsolated()); const GURL url_a1("http://www.google.com/1.html"); scoped_refptr<SiteInstanceImpl> site_instance_a1( browsing_instance->GetSiteInstanceForURL( UrlInfo::CreateForTesting(url_a1), false)); EXPECT_TRUE(site_instance_a1.get() != nullptr); // A separate site should create a separate SiteInstance. const GURL url_b1("http://www.yahoo.com/"); scoped_refptr<SiteInstanceImpl> site_instance_b1( browsing_instance->GetSiteInstanceForURL( UrlInfo::CreateForTesting(url_b1), false)); EXPECT_NE(site_instance_a1.get(), site_instance_b1.get()); EXPECT_TRUE(site_instance_a1->IsRelatedSiteInstance(site_instance_b1.get())); // Getting the new SiteInstance from the BrowsingInstance and from another // SiteInstance in the BrowsingInstance should give the same result. EXPECT_EQ(site_instance_b1.get(), site_instance_a1->GetRelatedSiteInstance(url_b1)); // A second visit to the original site should return the same SiteInstance. const GURL url_a2("http://www.google.com/2.html"); EXPECT_EQ(site_instance_a1.get(), browsing_instance->GetSiteInstanceForURL( UrlInfo::CreateForTesting(url_a2), false)); EXPECT_EQ(site_instance_a1.get(), site_instance_a1->GetRelatedSiteInstance(url_a2)); // A visit to the original site in a new BrowsingInstance (same or different // browser context) should return a different SiteInstance. BrowsingInstance* browsing_instance2 = new BrowsingInstance( browser_context.get(), WebExposedIsolationInfo::CreateNonIsolated()); // Ensure the new SiteInstance is ref counted so that it gets deleted. scoped_refptr<SiteInstanceImpl> site_instance_a2_2( browsing_instance2->GetSiteInstanceForURL( UrlInfo::CreateForTesting(url_a2), false)); EXPECT_NE(site_instance_a1.get(), site_instance_a2_2.get()); EXPECT_FALSE( site_instance_a1->IsRelatedSiteInstance(site_instance_a2_2.get())); // The two SiteInstances for http://google.com should not use the same process // if process-per-site is not enabled. RenderProcessHost* process_a1 = site_instance_a1->GetProcess(); RenderProcessHost* process_a2_2 = site_instance_a2_2->GetProcess(); EXPECT_NE(process_a1, process_a2_2); // Should be able to see that we do have SiteInstances. EXPECT_TRUE(browsing_instance->HasSiteInstance( GetSiteInfoForURL("http://mail.google.com"))); EXPECT_TRUE(browsing_instance2->HasSiteInstance( GetSiteInfoForURL("http://mail.google.com"))); EXPECT_TRUE(browsing_instance->HasSiteInstance( GetSiteInfoForURL("http://mail.yahoo.com"))); // Should be able to see that we don't have SiteInstances. EXPECT_FALSE(browsing_instance->HasSiteInstance( GetSiteInfoForURL("https://www.google.com"))); EXPECT_FALSE(browsing_instance2->HasSiteInstance( GetSiteInfoForURL("http://www.yahoo.com"))); // browsing_instances will be deleted when their SiteInstances are deleted. // The processes will be unregistered when the RPH scoped_ptrs go away. DrainMessageLoop(); } // Test to ensure that there is only one RenderProcessHost per site for an // entire BrowserContext, if process-per-site is in use. TEST_F(SiteInstanceTest, OneSiteInstancePerSiteInBrowserContext) { base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kProcessPerSite); std::unique_ptr<TestBrowserContext> browser_context(new TestBrowserContext()); scoped_refptr<BrowsingInstance> browsing_instance = new BrowsingInstance( browser_context.get(), WebExposedIsolationInfo::CreateNonIsolated()); const GURL url_a1("http://www.google.com/1.html"); scoped_refptr<SiteInstanceImpl> site_instance_a1( browsing_instance->GetSiteInstanceForURL( UrlInfo::CreateForTesting(url_a1), false)); EXPECT_TRUE(site_instance_a1.get() != nullptr); RenderProcessHost* process_a1 = site_instance_a1->GetProcess(); // A separate site should create a separate SiteInstance. const GURL url_b1("http://www.yahoo.com/"); scoped_refptr<SiteInstanceImpl> site_instance_b1( browsing_instance->GetSiteInstanceForURL( UrlInfo::CreateForTesting(url_b1), false)); EXPECT_NE(site_instance_a1.get(), site_instance_b1.get()); EXPECT_TRUE(site_instance_a1->IsRelatedSiteInstance(site_instance_b1.get())); // Getting the new SiteInstance from the BrowsingInstance and from another // SiteInstance in the BrowsingInstance should give the same result. EXPECT_EQ(site_instance_b1.get(), site_instance_a1->GetRelatedSiteInstance(url_b1)); // A second visit to the original site should return the same SiteInstance. const GURL url_a2("http://www.google.com/2.html"); EXPECT_EQ(site_instance_a1.get(), browsing_instance->GetSiteInstanceForURL( UrlInfo::CreateForTesting(url_a2), false)); EXPECT_EQ(site_instance_a1.get(), site_instance_a1->GetRelatedSiteInstance(url_a2)); // A visit to the original site in a new BrowsingInstance (same browser // context) should return a different SiteInstance with the same process. BrowsingInstance* browsing_instance2 = new BrowsingInstance( browser_context.get(), WebExposedIsolationInfo::CreateNonIsolated()); scoped_refptr<SiteInstanceImpl> site_instance_a1_2( browsing_instance2->GetSiteInstanceForURL( UrlInfo::CreateForTesting(url_a1), false)); EXPECT_TRUE(site_instance_a1.get() != nullptr); EXPECT_NE(site_instance_a1.get(), site_instance_a1_2.get()); EXPECT_EQ(process_a1, site_instance_a1_2->GetProcess()); // A visit to the original site in a new BrowsingInstance (different browser // context) should return a different SiteInstance with a different process. std::unique_ptr<TestBrowserContext> browser_context2( new TestBrowserContext()); BrowsingInstance* browsing_instance3 = new BrowsingInstance( browser_context2.get(), WebExposedIsolationInfo::CreateNonIsolated()); scoped_refptr<SiteInstanceImpl> site_instance_a2_3( browsing_instance3->GetSiteInstanceForURL( UrlInfo::CreateForTesting(url_a2), false)); EXPECT_TRUE(site_instance_a2_3.get() != nullptr); RenderProcessHost* process_a2_3 = site_instance_a2_3->GetProcess(); EXPECT_NE(site_instance_a1.get(), site_instance_a2_3.get()); EXPECT_NE(process_a1, process_a2_3); // Should be able to see that we do have SiteInstances. EXPECT_TRUE(browsing_instance->HasSiteInstance( GetSiteInfoForURL("http://mail.google.com"))); // visited before EXPECT_TRUE(browsing_instance2->HasSiteInstance( GetSiteInfoForURL("http://mail.google.com"))); // visited before EXPECT_TRUE(browsing_instance->HasSiteInstance( GetSiteInfoForURL("http://mail.yahoo.com"))); // visited before // Should be able to see that we don't have SiteInstances. EXPECT_FALSE(browsing_instance2->HasSiteInstance(GetSiteInfoForURL( "http://www.yahoo.com"))); // different BI, same browser context EXPECT_FALSE(browsing_instance->HasSiteInstance( GetSiteInfoForURL("https://www.google.com"))); // not visited before EXPECT_FALSE(browsing_instance3->HasSiteInstance(GetSiteInfoForURL( "http://www.yahoo.com"))); // different BI, different context // browsing_instances will be deleted when their SiteInstances are deleted. // The processes will be unregistered when the RPH scoped_ptrs go away. DrainMessageLoop(); } // Test to ensure that IsSuitableForUrlInfo behaves properly for different types // of URLs. TEST_F(SiteInstanceTest, IsSuitableForUrlInfo) { std::unique_ptr<TestBrowserContext> browser_context(new TestBrowserContext()); RenderProcessHost* host; scoped_refptr<SiteInstanceImpl> instance( SiteInstanceImpl::Create(browser_context.get())); EXPECT_FALSE(instance->HasSite()); EXPECT_TRUE(instance->GetSiteURL().is_empty()); // Check prior to assigning a site or process to the instance, which is // expected to return false to allow the SiteInstance to be used for anything. EXPECT_TRUE(instance->IsSuitableForUrlInfo( UrlInfo::CreateForTesting(GURL("http://google.com")))); instance->SetSite(UrlInfo::CreateForTesting(GURL("http://evernote.com/"))); EXPECT_TRUE(instance->HasSite()); // The call to GetProcess actually creates a new real process, which works // fine, but might be a cause for problems in different contexts. host = instance->GetProcess(); EXPECT_TRUE(host != nullptr); EXPECT_TRUE(instance->HasProcess()); EXPECT_TRUE(instance->IsSuitableForUrlInfo( UrlInfo::CreateForTesting(GURL("http://evernote.com")))); EXPECT_TRUE(instance->IsSuitableForUrlInfo(UrlInfo::CreateForTesting( GURL("javascript:alert(document.location.href);")))); EXPECT_FALSE(instance->IsSuitableForUrlInfo( UrlInfo::CreateForTesting(GetWebUIURL(kChromeUIGpuHost)))); // Test that WebUI SiteInstances reject normal web URLs. const GURL webui_url(GetWebUIURL(kChromeUIGpuHost)); scoped_refptr<SiteInstanceImpl> webui_instance( SiteInstanceImpl::Create(browser_context.get())); webui_instance->SetSite(UrlInfo::CreateForTesting(webui_url)); RenderProcessHost* webui_host = webui_instance->GetProcess(); // Simulate granting WebUI bindings for the process. ChildProcessSecurityPolicyImpl::GetInstance()->GrantWebUIBindings( webui_host->GetID(), BINDINGS_POLICY_WEB_UI); EXPECT_TRUE(webui_instance->HasProcess()); EXPECT_TRUE(webui_instance->IsSuitableForUrlInfo( UrlInfo::CreateForTesting(webui_url))); EXPECT_FALSE(webui_instance->IsSuitableForUrlInfo( UrlInfo::CreateForTesting(GURL("http://google.com")))); EXPECT_FALSE(webui_instance->IsSuitableForUrlInfo( UrlInfo::CreateForTesting(GURL("http://gpu")))); // WebUI uses process-per-site, so another instance will use the same process // even if we haven't called GetProcess yet. Make sure IsSuitableForUrlInfo // doesn't crash (http://crbug.com/137070). scoped_refptr<SiteInstanceImpl> webui_instance2( SiteInstanceImpl::Create(browser_context.get())); webui_instance2->SetSite(UrlInfo::CreateForTesting(webui_url)); EXPECT_TRUE(webui_instance2->IsSuitableForUrlInfo( UrlInfo::CreateForTesting(webui_url))); EXPECT_FALSE(webui_instance2->IsSuitableForUrlInfo( UrlInfo::CreateForTesting(GURL("http://google.com")))); DrainMessageLoop(); } // Test to ensure that IsSuitableForUrlInfo behaves properly even when // --site-per-process is used (http://crbug.com/160671). TEST_F(SiteInstanceTest, IsSuitableForUrlInfoInSitePerProcess) { IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess()); std::unique_ptr<TestBrowserContext> browser_context(new TestBrowserContext()); RenderProcessHost* host; scoped_refptr<SiteInstanceImpl> instance( SiteInstanceImpl::Create(browser_context.get())); // Check prior to assigning a site or process to the instance, which is // expected to return false to allow the SiteInstance to be used for anything. EXPECT_TRUE(instance->IsSuitableForUrlInfo( UrlInfo::CreateForTesting(GURL("http://google.com")))); instance->SetSite(UrlInfo::CreateForTesting(GURL("http://evernote.com/"))); EXPECT_TRUE(instance->HasSite()); // The call to GetProcess actually creates a new real process, which works // fine, but might be a cause for problems in different contexts. host = instance->GetProcess(); EXPECT_TRUE(host != nullptr); EXPECT_TRUE(instance->HasProcess()); EXPECT_TRUE(instance->IsSuitableForUrlInfo( UrlInfo::CreateForTesting(GURL("http://evernote.com")))); EXPECT_TRUE(instance->IsSuitableForUrlInfo(UrlInfo::CreateForTesting( GURL("javascript:alert(document.location.href);")))); EXPECT_FALSE(instance->IsSuitableForUrlInfo( UrlInfo::CreateForTesting(GetWebUIURL(kChromeUIGpuHost)))); DrainMessageLoop(); } // Test that we do not reuse a process in process-per-site mode if it has the // wrong bindings for its URL. http://crbug.com/174059. TEST_F(SiteInstanceTest, ProcessPerSiteWithWrongBindings) { std::unique_ptr<TestBrowserContext> browser_context(new TestBrowserContext()); RenderProcessHost* host; RenderProcessHost* host2; scoped_refptr<SiteInstanceImpl> instance( SiteInstanceImpl::Create(browser_context.get())); EXPECT_FALSE(instance->HasSite()); EXPECT_TRUE(instance->GetSiteURL().is_empty()); // Simulate navigating to a WebUI URL in a process that does not have WebUI // bindings. This already requires bypassing security checks. const GURL webui_url(GetWebUIURL(kChromeUIGpuHost)); instance->SetSite(UrlInfo::CreateForTesting(webui_url)); EXPECT_TRUE(instance->HasSite()); // The call to GetProcess actually creates a new real process. host = instance->GetProcess(); EXPECT_TRUE(host != nullptr); EXPECT_TRUE(instance->HasProcess()); // Without bindings, this should look like the wrong process. EXPECT_FALSE( instance->IsSuitableForUrlInfo(UrlInfo::CreateForTesting(webui_url))); // WebUI uses process-per-site, so another instance would normally use the // same process. Make sure it doesn't use the same process if the bindings // are missing. scoped_refptr<SiteInstanceImpl> instance2( SiteInstanceImpl::Create(browser_context.get())); instance2->SetSite(UrlInfo::CreateForTesting(webui_url)); host2 = instance2->GetProcess(); EXPECT_TRUE(host2 != nullptr); EXPECT_TRUE(instance2->HasProcess()); EXPECT_NE(host, host2); DrainMessageLoop(); } // Test that we do not register processes with empty sites for process-per-site // mode. TEST_F(SiteInstanceTest, NoProcessPerSiteForEmptySite) { base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kProcessPerSite); std::unique_ptr<TestBrowserContext> browser_context(new TestBrowserContext()); scoped_refptr<SiteInstanceImpl> instance( SiteInstanceImpl::Create(browser_context.get())); instance->SetSite(UrlInfo()); EXPECT_TRUE(instance->HasSite()); EXPECT_TRUE(instance->GetSiteURL().is_empty()); instance->GetProcess(); EXPECT_FALSE(RenderProcessHostImpl::GetSoleProcessHostForSite( instance->GetIsolationContext(), SiteInfo(browser_context.get()))); DrainMessageLoop(); } // Check that an URL is considered same-site with blob: and filesystem: URLs // with a matching inner origin. See https://crbug.com/726370. TEST_F(SiteInstanceTest, IsSameSiteForNestedURLs) { TestBrowserContext context; GURL foo_url("http://foo.com/"); GURL bar_url("http://bar.com/"); GURL blob_foo_url("blob:http://foo.com/uuid"); GURL blob_bar_url("blob:http://bar.com/uuid"); GURL fs_foo_url("filesystem:http://foo.com/path/"); GURL fs_bar_url("filesystem:http://bar.com/path/"); EXPECT_TRUE(IsSameSite(&context, foo_url, blob_foo_url)); EXPECT_TRUE(IsSameSite(&context, blob_foo_url, foo_url)); EXPECT_FALSE(IsSameSite(&context, foo_url, blob_bar_url)); EXPECT_FALSE(IsSameSite(&context, blob_foo_url, bar_url)); EXPECT_TRUE(IsSameSite(&context, foo_url, fs_foo_url)); EXPECT_TRUE(IsSameSite(&context, fs_foo_url, foo_url)); EXPECT_FALSE(IsSameSite(&context, foo_url, fs_bar_url)); EXPECT_FALSE(IsSameSite(&context, fs_foo_url, bar_url)); EXPECT_TRUE(IsSameSite(&context, blob_foo_url, fs_foo_url)); EXPECT_FALSE(IsSameSite(&context, blob_foo_url, fs_bar_url)); EXPECT_FALSE(IsSameSite(&context, blob_foo_url, blob_bar_url)); EXPECT_FALSE(IsSameSite(&context, fs_foo_url, fs_bar_url)); // Verify that the scheme and ETLD+1 are used for comparison. GURL www_bar_url("http://www.bar.com/"); GURL bar_org_url("http://bar.org/"); GURL https_bar_url("https://bar.com/"); EXPECT_TRUE(IsSameSite(&context, www_bar_url, bar_url)); EXPECT_TRUE(IsSameSite(&context, www_bar_url, blob_bar_url)); EXPECT_TRUE(IsSameSite(&context, www_bar_url, fs_bar_url)); EXPECT_FALSE(IsSameSite(&context, bar_org_url, bar_url)); EXPECT_FALSE(IsSameSite(&context, bar_org_url, blob_bar_url)); EXPECT_FALSE(IsSameSite(&context, bar_org_url, fs_bar_url)); EXPECT_FALSE(IsSameSite(&context, https_bar_url, bar_url)); EXPECT_FALSE(IsSameSite(&context, https_bar_url, blob_bar_url)); EXPECT_FALSE(IsSameSite(&context, https_bar_url, fs_bar_url)); } TEST_F(SiteInstanceTest, StrictOriginIsolation) { base::test::ScopedFeatureList feature_list; feature_list.InitAndEnableFeature(features::kStrictOriginIsolation); EXPECT_TRUE(base::FeatureList::IsEnabled(features::kStrictOriginIsolation)); GURL isolated1_foo_url("http://isolated1.foo.com"); GURL isolated2_foo_url("http://isolated2.foo.com"); TestBrowserContext browser_context; IsolationContext isolation_context(&browser_context); EXPECT_FALSE(IsSameSite(context(), isolated1_foo_url, isolated2_foo_url)); EXPECT_NE(GetSiteForURL(isolation_context, isolated1_foo_url), GetSiteForURL(isolation_context, isolated2_foo_url)); // A bunch of special cases of origins. GURL secure_foo("https://foo.com"); EXPECT_EQ(GetSiteForURL(isolation_context, secure_foo), secure_foo); GURL foo_with_port("http://foo.com:1234"); EXPECT_EQ(GetSiteForURL(isolation_context, foo_with_port), foo_with_port); GURL local_host("http://localhost"); EXPECT_EQ(GetSiteForURL(isolation_context, local_host), local_host); GURL ip_local_host("http://127.0.0.1"); EXPECT_EQ(GetSiteForURL(isolation_context, ip_local_host), ip_local_host); // The following should not get origin-specific SiteInstances, as they don't // have valid hosts. GURL about_url("about:flags"); EXPECT_NE(GetSiteForURL(isolation_context, about_url), about_url); GURL file_url("file:///home/user/foo"); EXPECT_NE(GetSiteForURL(isolation_context, file_url), file_url); } TEST_F(SiteInstanceTest, IsolatedOrigins) { GURL foo_url("http://www.foo.com"); GURL isolated_foo_url("http://isolated.foo.com"); GURL isolated_bar_url("http://isolated.bar.com"); auto* policy = ChildProcessSecurityPolicyImpl::GetInstance(); EXPECT_FALSE(IsIsolatedOrigin(isolated_foo_url)); EXPECT_TRUE(IsSameSite(context(), foo_url, isolated_foo_url)); policy->AddFutureIsolatedOrigins({url::Origin::Create(isolated_foo_url)}, IsolatedOriginSource::TEST); EXPECT_TRUE(IsIsolatedOrigin(isolated_foo_url)); EXPECT_FALSE(IsIsolatedOrigin(foo_url)); EXPECT_FALSE(IsIsolatedOrigin(GURL("http://foo.com"))); EXPECT_FALSE(IsIsolatedOrigin(GURL("http://www.bar.com"))); EXPECT_TRUE(IsIsolatedOrigin(isolated_foo_url)); EXPECT_FALSE(IsIsolatedOrigin(foo_url)); EXPECT_FALSE(IsIsolatedOrigin(GURL("http://foo.com"))); EXPECT_FALSE(IsIsolatedOrigin(GURL("http://www.bar.com"))); // Different scheme. EXPECT_FALSE(IsIsolatedOrigin(GURL("https://isolated.foo.com"))); // Different port. EXPECT_TRUE(IsIsolatedOrigin(GURL("http://isolated.foo.com:12345"))); policy->AddFutureIsolatedOrigins({url::Origin::Create(isolated_bar_url)}, IsolatedOriginSource::TEST); EXPECT_TRUE(IsIsolatedOrigin(isolated_bar_url)); // IsSameSite should compare origins rather than sites if either URL is an // isolated origin. EXPECT_FALSE(IsSameSite(context(), foo_url, isolated_foo_url)); EXPECT_FALSE(IsSameSite(context(), isolated_foo_url, foo_url)); EXPECT_FALSE(IsSameSite(context(), isolated_foo_url, isolated_bar_url)); EXPECT_TRUE(IsSameSite(context(), isolated_foo_url, isolated_foo_url)); // Ensure blob and filesystem URLs with isolated origins are compared // correctly. GURL isolated_blob_foo_url("blob:http://isolated.foo.com/uuid"); EXPECT_TRUE(IsSameSite(context(), isolated_foo_url, isolated_blob_foo_url)); GURL isolated_filesystem_foo_url("filesystem:http://isolated.foo.com/bar/"); EXPECT_TRUE( IsSameSite(context(), isolated_foo_url, isolated_filesystem_foo_url)); // The site URL for an isolated origin should be the full origin rather than // eTLD+1. IsolationContext isolation_context(context()); EXPECT_EQ(isolated_foo_url, GetSiteForURL(isolation_context, isolated_foo_url)); EXPECT_EQ( isolated_foo_url, GetSiteForURL(isolation_context, GURL("http://isolated.foo.com:12345"))); EXPECT_EQ(isolated_bar_url, GetSiteForURL(isolation_context, isolated_bar_url)); EXPECT_EQ(isolated_foo_url, GetSiteForURL(isolation_context, isolated_blob_foo_url)); EXPECT_EQ(isolated_foo_url, GetSiteForURL(isolation_context, isolated_filesystem_foo_url)); // Isolated origins always require a dedicated process. EXPECT_TRUE( DoesURLRequireDedicatedProcess(isolation_context, isolated_foo_url)); EXPECT_TRUE( DoesURLRequireDedicatedProcess(isolation_context, isolated_bar_url)); EXPECT_TRUE( DoesURLRequireDedicatedProcess(isolation_context, isolated_blob_foo_url)); EXPECT_TRUE(DoesURLRequireDedicatedProcess(isolation_context, isolated_filesystem_foo_url)); // Cleanup. policy->RemoveIsolatedOriginForTesting(url::Origin::Create(isolated_foo_url)); policy->RemoveIsolatedOriginForTesting(url::Origin::Create(isolated_bar_url)); } TEST_F(SiteInstanceTest, IsolatedOriginsWithPort) { GURL isolated_foo_url("http://isolated.foo.com"); GURL isolated_foo_with_port("http://isolated.foo.com:12345"); auto* policy = ChildProcessSecurityPolicyImpl::GetInstance(); { base::test::MockLog mock_log; EXPECT_CALL( mock_log, Log(::logging::LOG_ERROR, testing::_, testing::_, testing::_, ::testing::HasSubstr("Ignoring port number in isolated origin: " "http://isolated.foo.com:12345"))) .Times(1); mock_log.StartCapturingLogs(); policy->AddFutureIsolatedOrigins( {url::Origin::Create(isolated_foo_with_port)}, IsolatedOriginSource::TEST); } EXPECT_TRUE(IsIsolatedOrigin(isolated_foo_url)); EXPECT_TRUE(IsIsolatedOrigin(isolated_foo_with_port)); IsolationContext isolation_context(context()); EXPECT_EQ(isolated_foo_url, GetSiteForURL(isolation_context, isolated_foo_url)); EXPECT_EQ(isolated_foo_url, GetSiteForURL(isolation_context, isolated_foo_with_port)); // Cleanup. policy->RemoveIsolatedOriginForTesting(url::Origin::Create(isolated_foo_url)); policy->RemoveIsolatedOriginForTesting( url::Origin::Create(isolated_foo_with_port)); } // Check that only valid isolated origins are allowed to be registered. TEST_F(SiteInstanceTest, IsValidIsolatedOrigin) { // Unique origins are invalid, as are invalid URLs that resolve to // unique origins. EXPECT_FALSE(IsolatedOriginUtil::IsValidIsolatedOrigin(url::Origin())); EXPECT_FALSE(IsolatedOriginUtil::IsValidIsolatedOrigin( url::Origin::Create(GURL("invalid.url")))); // IP addresses are ok. EXPECT_TRUE(IsolatedOriginUtil::IsValidIsolatedOrigin( url::Origin::Create(GURL("http://127.0.0.1")))); // Hosts without a valid registry-controlled domain are disallowed. This // includes hosts that are themselves a registry-controlled domain. EXPECT_FALSE(IsolatedOriginUtil::IsValidIsolatedOrigin( url::Origin::Create(GURL("http://.com/")))); EXPECT_FALSE(IsolatedOriginUtil::IsValidIsolatedOrigin( url::Origin::Create(GURL("http://.com./")))); EXPECT_FALSE(IsolatedOriginUtil::IsValidIsolatedOrigin( url::Origin::Create(GURL("http://foo/")))); EXPECT_FALSE(IsolatedOriginUtil::IsValidIsolatedOrigin( url::Origin::Create(GURL("http://co.uk/")))); EXPECT_TRUE(IsolatedOriginUtil::IsValidIsolatedOrigin( url::Origin::Create(GURL("http://foo.bar.baz/")))); // Scheme must be HTTP or HTTPS. EXPECT_FALSE(IsolatedOriginUtil::IsValidIsolatedOrigin( url::Origin::Create(GetWebUIURL(kChromeUIGpuHost)))); EXPECT_TRUE(IsolatedOriginUtil::IsValidIsolatedOrigin( url::Origin::Create(GURL("http://a.com")))); EXPECT_TRUE(IsolatedOriginUtil::IsValidIsolatedOrigin( url::Origin::Create(GURL("https://b.co.uk")))); // Trailing dot is disallowed. EXPECT_FALSE(IsolatedOriginUtil::IsValidIsolatedOrigin( url::Origin::Create(GURL("http://a.com.")))); } TEST_F(SiteInstanceTest, SubdomainOnIsolatedSite) { GURL isolated_url("http://isolated.com"); GURL foo_isolated_url("http://foo.isolated.com"); auto* policy = ChildProcessSecurityPolicyImpl::GetInstance(); policy->AddFutureIsolatedOrigins({url::Origin::Create(isolated_url)}, IsolatedOriginSource::TEST); EXPECT_TRUE(IsIsolatedOrigin(isolated_url)); EXPECT_TRUE(IsIsolatedOrigin(foo_isolated_url)); EXPECT_FALSE(IsIsolatedOrigin(GURL("http://unisolated.com"))); EXPECT_FALSE(IsIsolatedOrigin(GURL("http://isolated.foo.com"))); // Wrong scheme. EXPECT_FALSE(IsIsolatedOrigin(GURL("https://foo.isolated.com"))); // Subdomain with a different port. EXPECT_TRUE(IsIsolatedOrigin(GURL("http://foo.isolated.com:12345"))); // Appending a trailing dot to a URL should not bypass process isolation. EXPECT_TRUE(IsIsolatedOrigin(GURL("http://isolated.com."))); EXPECT_TRUE(IsIsolatedOrigin(GURL("http://foo.isolated.com."))); // A new SiteInstance created for a subdomain on an isolated origin // should use the isolated origin's host and not its own host as the site // URL. IsolationContext isolation_context(context()); EXPECT_EQ(isolated_url, GetSiteForURL(isolation_context, foo_isolated_url)); EXPECT_TRUE( DoesURLRequireDedicatedProcess(isolation_context, foo_isolated_url)); EXPECT_TRUE(IsSameSite(context(), isolated_url, foo_isolated_url)); EXPECT_TRUE(IsSameSite(context(), foo_isolated_url, isolated_url)); // Don't try to match subdomains on IP addresses. GURL isolated_ip("http://127.0.0.1"); policy->AddFutureIsolatedOrigins({url::Origin::Create(isolated_ip)}, IsolatedOriginSource::TEST); EXPECT_TRUE(IsIsolatedOrigin(isolated_ip)); EXPECT_FALSE(IsIsolatedOrigin(GURL("http://42.127.0.0.1"))); // Cleanup. policy->RemoveIsolatedOriginForTesting(url::Origin::Create(isolated_url)); policy->RemoveIsolatedOriginForTesting(url::Origin::Create(isolated_ip)); } TEST_F(SiteInstanceTest, SubdomainOnIsolatedOrigin) { GURL foo_url("http://foo.com"); GURL isolated_foo_url("http://isolated.foo.com"); GURL bar_isolated_foo_url("http://bar.isolated.foo.com"); GURL baz_isolated_foo_url("http://baz.isolated.foo.com"); auto* policy = ChildProcessSecurityPolicyImpl::GetInstance(); policy->AddFutureIsolatedOrigins({url::Origin::Create(isolated_foo_url)}, IsolatedOriginSource::TEST); EXPECT_FALSE(IsIsolatedOrigin(foo_url)); EXPECT_TRUE(IsIsolatedOrigin(isolated_foo_url)); EXPECT_TRUE(IsIsolatedOrigin(bar_isolated_foo_url)); EXPECT_TRUE(IsIsolatedOrigin(baz_isolated_foo_url)); IsolationContext isolation_context(context()); EXPECT_EQ(foo_url, GetSiteForURL(isolation_context, foo_url)); EXPECT_EQ(isolated_foo_url, GetSiteForURL(isolation_context, isolated_foo_url)); EXPECT_EQ(isolated_foo_url, GetSiteForURL(isolation_context, bar_isolated_foo_url)); EXPECT_EQ(isolated_foo_url, GetSiteForURL(isolation_context, baz_isolated_foo_url)); if (!AreAllSitesIsolatedForTesting()) { EXPECT_FALSE(DoesURLRequireDedicatedProcess(isolation_context, foo_url)); } EXPECT_TRUE( DoesURLRequireDedicatedProcess(isolation_context, isolated_foo_url)); EXPECT_TRUE( DoesURLRequireDedicatedProcess(isolation_context, bar_isolated_foo_url)); EXPECT_TRUE( DoesURLRequireDedicatedProcess(isolation_context, baz_isolated_foo_url)); EXPECT_FALSE(IsSameSite(context(), foo_url, isolated_foo_url)); EXPECT_FALSE(IsSameSite(context(), isolated_foo_url, foo_url)); EXPECT_FALSE(IsSameSite(context(), foo_url, bar_isolated_foo_url)); EXPECT_FALSE(IsSameSite(context(), bar_isolated_foo_url, foo_url)); EXPECT_TRUE(IsSameSite(context(), bar_isolated_foo_url, isolated_foo_url)); EXPECT_TRUE(IsSameSite(context(), isolated_foo_url, bar_isolated_foo_url)); EXPECT_TRUE( IsSameSite(context(), bar_isolated_foo_url, baz_isolated_foo_url)); EXPECT_TRUE( IsSameSite(context(), baz_isolated_foo_url, bar_isolated_foo_url)); // Cleanup. policy->RemoveIsolatedOriginForTesting(url::Origin::Create(isolated_foo_url)); } TEST_F(SiteInstanceTest, MultipleIsolatedOriginsWithCommonSite) { GURL foo_url("http://foo.com"); GURL bar_foo_url("http://bar.foo.com"); GURL baz_bar_foo_url("http://baz.bar.foo.com"); GURL qux_baz_bar_foo_url("http://qux.baz.bar.foo.com"); IsolationContext isolation_context(context()); auto* policy = ChildProcessSecurityPolicyImpl::GetInstance(); policy->AddFutureIsolatedOrigins( {url::Origin::Create(foo_url), url::Origin::Create(baz_bar_foo_url)}, IsolatedOriginSource::TEST); EXPECT_TRUE(IsIsolatedOrigin(foo_url)); EXPECT_TRUE(IsIsolatedOrigin(bar_foo_url)); EXPECT_TRUE(IsIsolatedOrigin(baz_bar_foo_url)); EXPECT_TRUE(IsIsolatedOrigin(qux_baz_bar_foo_url)); EXPECT_EQ(foo_url, GetSiteForURL(isolation_context, foo_url)); EXPECT_EQ(foo_url, GetSiteForURL(isolation_context, bar_foo_url)); EXPECT_EQ(baz_bar_foo_url, GetSiteForURL(isolation_context, baz_bar_foo_url)); EXPECT_EQ(baz_bar_foo_url, GetSiteForURL(isolation_context, qux_baz_bar_foo_url)); EXPECT_TRUE(DoesURLRequireDedicatedProcess(isolation_context, foo_url)); EXPECT_TRUE(DoesURLRequireDedicatedProcess(isolation_context, bar_foo_url)); EXPECT_TRUE( DoesURLRequireDedicatedProcess(isolation_context, baz_bar_foo_url)); EXPECT_TRUE( DoesURLRequireDedicatedProcess(isolation_context, qux_baz_bar_foo_url)); EXPECT_TRUE(IsSameSite(context(), foo_url, bar_foo_url)); EXPECT_FALSE(IsSameSite(context(), foo_url, baz_bar_foo_url)); EXPECT_FALSE(IsSameSite(context(), foo_url, qux_baz_bar_foo_url)); EXPECT_FALSE(IsSameSite(context(), bar_foo_url, baz_bar_foo_url)); EXPECT_FALSE(IsSameSite(context(), bar_foo_url, qux_baz_bar_foo_url)); EXPECT_TRUE(IsSameSite(context(), baz_bar_foo_url, qux_baz_bar_foo_url)); // Cleanup. policy->RemoveIsolatedOriginForTesting(url::Origin::Create(foo_url)); policy->RemoveIsolatedOriginForTesting(url::Origin::Create(baz_bar_foo_url)); } // Check that new SiteInstances correctly preserve the full URL that was used // to initialize their site URL. TEST_F(SiteInstanceTest, OriginalURL) { GURL original_url("https://foo.com/"); GURL app_url("https://app.com/"); EffectiveURLContentBrowserClient modified_client( original_url, app_url, /* requires_dedicated_process */ true); ContentBrowserClient* regular_client = SetBrowserClientForTesting(&modified_client); std::unique_ptr<TestBrowserContext> browser_context(new TestBrowserContext()); SiteInfo expected_site_info( app_url /* site_url */, original_url /* process_lock_url */, false /* requires_origin_keyed_process */, CreateStoragePartitionConfigForTesting(), WebExposedIsolationInfo::CreateNonIsolated(), false /* is_guest */, false /* does_site_request_dedicated_process_for_coop */, false /* is_jit_disabled */, false /* is_pdf */); // New SiteInstance in a new BrowsingInstance with a predetermined URL. In // this and subsequent cases, the site URL should consist of the effective // URL's site, and the process lock URL and original URLs should be based on // |original_url|. { scoped_refptr<SiteInstanceImpl> site_instance = SiteInstanceImpl::CreateForTesting(browser_context.get(), original_url); EXPECT_EQ(expected_site_info, site_instance->GetSiteInfo()); EXPECT_EQ(original_url, site_instance->original_url()); } // New related SiteInstance from an existing SiteInstance with a // predetermined URL. { scoped_refptr<SiteInstanceImpl> bar_site_instance = SiteInstanceImpl::CreateForTesting(browser_context.get(), GURL("https://bar.com/")); scoped_refptr<SiteInstance> site_instance = bar_site_instance->GetRelatedSiteInstance(original_url); auto* site_instance_impl = static_cast<SiteInstanceImpl*>(site_instance.get()); EXPECT_EQ(expected_site_info, site_instance_impl->GetSiteInfo()); EXPECT_EQ(original_url, site_instance_impl->original_url()); } // New SiteInstance with a lazily assigned site URL. { scoped_refptr<SiteInstanceImpl> site_instance = SiteInstanceImpl::Create(browser_context.get()); EXPECT_FALSE(site_instance->HasSite()); EXPECT_TRUE(site_instance->original_url().is_empty()); site_instance->SetSite(UrlInfo::CreateForTesting(original_url)); EXPECT_EQ(expected_site_info, site_instance->GetSiteInfo()); EXPECT_EQ(original_url, site_instance->original_url()); } SetBrowserClientForTesting(regular_client); } namespace { ProcessLock ProcessLockFromString(const std::string& url) { return ProcessLock::FromSiteInfo(SiteInfo( GURL(url), GURL(url), false /* requires_origin_keyed_process */, CreateStoragePartitionConfigForTesting(), WebExposedIsolationInfo::CreateNonIsolated(), false /* is_guest */, false /* does_site_request_dedicated_process_for_coop */, false /* is_jit_disabled */, false /* is_pdf */)); } } // namespace TEST_F(SiteInstanceTest, IsProcessLockASite) { EXPECT_FALSE(ProcessLockFromString("http://").IsASiteOrOrigin()); EXPECT_FALSE(ProcessLockFromString("").IsASiteOrOrigin()); EXPECT_FALSE(ProcessLockFromString("google.com").IsASiteOrOrigin()); EXPECT_FALSE(ProcessLockFromString("http:").IsASiteOrOrigin()); EXPECT_FALSE(ProcessLockFromString("chrome:").IsASiteOrOrigin()); EXPECT_TRUE(ProcessLockFromString("http://foo.com").IsASiteOrOrigin()); EXPECT_TRUE(ProcessLockFromString("http://bar.foo.com").IsASiteOrOrigin()); EXPECT_TRUE( ProcessLockFromString("http://user:pass@google.com:99/foo;bar?q=a#ref") .IsASiteOrOrigin()); } TEST_F(SiteInstanceTest, StartIsolatingSite) { // Skip this test case if dynamic isolated origins are not enabled. if (!SiteIsolationPolicy::AreDynamicIsolatedOriginsEnabled()) return; IsolationContext isolation_context(context()); auto* policy = ChildProcessSecurityPolicyImpl::GetInstance(); // StartIsolatingSite() should convert the URL to a site before isolating it. SiteInstance::StartIsolatingSite( context(), GURL("http://bar.foo.com/foo/html.bar"), ChildProcessSecurityPolicy::IsolatedOriginSource::TEST); EXPECT_TRUE(IsIsolatedOrigin(GURL("http://foo.com"))); SiteInstance::StartIsolatingSite( context(), GURL("https://a.b.c.com:8000/"), ChildProcessSecurityPolicy::IsolatedOriginSource::TEST); EXPECT_TRUE(IsIsolatedOrigin(GURL("https://c.com"))); SiteInstance::StartIsolatingSite( context(), GURL("http://bar.com/foo/bar.html"), ChildProcessSecurityPolicy::IsolatedOriginSource::TEST); EXPECT_TRUE(IsIsolatedOrigin(GURL("http://bar.com"))); // Attempts to isolate an unsupported isolated origin should be ignored. GURL data_url("data:,"); GURL blank_url(url::kAboutBlankURL); SiteInstance::StartIsolatingSite( context(), data_url, ChildProcessSecurityPolicy::IsolatedOriginSource::TEST); SiteInstance::StartIsolatingSite( context(), blank_url, ChildProcessSecurityPolicy::IsolatedOriginSource::TEST); EXPECT_FALSE(IsIsolatedOrigin(data_url)); EXPECT_FALSE(IsIsolatedOrigin(blank_url)); // Cleanup. policy->RemoveStateForBrowserContext(*context()); } TEST_F(SiteInstanceTest, CreateForUrlInfo) { class CustomBrowserClient : public EffectiveURLContentBrowserClient { public: CustomBrowserClient(const GURL& url_to_modify, const GURL& url_to_return) : EffectiveURLContentBrowserClient(url_to_modify, url_to_return, false) {} void set_should_not_assign_url(const GURL& url) { should_not_assign_url_ = url; } bool ShouldAssignSiteForURL(const GURL& url) override { return url != should_not_assign_url_; } private: GURL should_not_assign_url_; }; const GURL kNonIsolatedUrl("https://bar.com/"); const GURL kIsolatedUrl("https://isolated.com/"); const GURL kFileUrl("file:///C:/Downloads/"); const GURL kCustomUrl("http://custom.foo.com"); const GURL kCustomAppUrl(std::string(kCustomStandardScheme) + "://custom"); CustomBrowserClient modified_client(kCustomUrl, kCustomAppUrl); ContentBrowserClient* regular_client = SetBrowserClientForTesting(&modified_client); ChildProcessSecurityPolicyImpl::GetInstance()->AddFutureIsolatedOrigins( {url::Origin::Create(kIsolatedUrl)}, IsolatedOriginSource::TEST); auto instance1 = SiteInstanceImpl::CreateForTesting(context(), kNonIsolatedUrl); auto instance2 = SiteInstanceImpl::CreateForTesting(context(), kIsolatedUrl); auto instance3 = SiteInstanceImpl::CreateForTesting(context(), kFileUrl); auto instance4 = SiteInstanceImpl::CreateForTesting(context(), GURL(url::kAboutBlankURL)); auto instance5 = SiteInstanceImpl::CreateForTesting(context(), kCustomUrl); if (AreDefaultSiteInstancesEnabled()) { EXPECT_TRUE(instance1->IsDefaultSiteInstance()); } else { EXPECT_FALSE(instance1->IsDefaultSiteInstance()); EXPECT_EQ(kNonIsolatedUrl, instance1->GetSiteURL()); } EXPECT_TRUE(instance1->DoesSiteInfoForURLMatch( UrlInfo::CreateForTesting(kNonIsolatedUrl))); EXPECT_TRUE(instance1->IsSameSiteWithURL(kNonIsolatedUrl)); EXPECT_FALSE(instance2->IsDefaultSiteInstance()); EXPECT_EQ(kIsolatedUrl, instance2->GetSiteURL()); EXPECT_TRUE(instance2->DoesSiteInfoForURLMatch( UrlInfo::CreateForTesting(kIsolatedUrl))); EXPECT_TRUE(instance2->IsSameSiteWithURL(kIsolatedUrl)); EXPECT_FALSE(instance3->IsDefaultSiteInstance()); EXPECT_EQ(GURL("file:"), instance3->GetSiteURL()); EXPECT_TRUE( instance3->DoesSiteInfoForURLMatch(UrlInfo::CreateForTesting(kFileUrl))); // Not same site because file URL's don't have a host. EXPECT_FALSE(instance3->IsSameSiteWithURL(kFileUrl)); // about:blank URLs generate a SiteInstance without the site URL set because // ShouldAssignSiteForURL() returns false and the expectation is that the // site URL will be set at a later time. EXPECT_FALSE(instance4->IsDefaultSiteInstance()); EXPECT_FALSE(instance4->HasSite()); EXPECT_FALSE(instance4->DoesSiteInfoForURLMatch( UrlInfo::CreateForTesting(GURL(url::kAboutBlankURL)))); EXPECT_FALSE(instance4->IsSameSiteWithURL(GURL(url::kAboutBlankURL))); // Test the standard effective URL case. EXPECT_TRUE(instance5->HasSite()); if (AreDefaultSiteInstancesEnabled()) { EXPECT_TRUE(instance5->IsDefaultSiteInstance()); } else { EXPECT_FALSE(instance5->IsDefaultSiteInstance()); EXPECT_EQ("custom-standard://custom/", instance5->GetSiteURL()); EXPECT_EQ("http://foo.com/", instance5->GetSiteInfo().process_lock_url()); } EXPECT_TRUE(instance5->DoesSiteInfoForURLMatch( UrlInfo::CreateForTesting(kCustomUrl))); EXPECT_TRUE(instance5->IsSameSiteWithURL(kCustomUrl)); // Test the "do not assign site" case with an effective URL. modified_client.set_should_not_assign_url(kCustomUrl); if (instance5->IsDefaultSiteInstance()) { // Verify that the default SiteInstance is no longer a site match // with |kCustomUrl| because this URL now requires a SiteInstance that // does not have its site set. EXPECT_FALSE(instance5->DoesSiteInfoForURLMatch( UrlInfo::CreateForTesting(kCustomUrl))); EXPECT_FALSE(instance5->IsSameSiteWithURL(kCustomUrl)); } // Verify that |kCustomUrl| will always construct a SiteInstance without // a site set now. auto instance6 = SiteInstanceImpl::CreateForTesting(context(), kCustomUrl); EXPECT_FALSE(instance6->IsDefaultSiteInstance()); EXPECT_FALSE(instance6->HasSite()); EXPECT_FALSE(instance6->DoesSiteInfoForURLMatch( UrlInfo::CreateForTesting(kCustomUrl))); EXPECT_FALSE(instance6->IsSameSiteWithURL(kCustomUrl)); SetBrowserClientForTesting(regular_client); } TEST_F(SiteInstanceTest, CreateForGuest) { const GURL kGuestUrl(std::string(kGuestScheme) + "://abc123/path"); // Verify that a SiteInstance created with CreateForUrlInfo() is not // considered a <webview> guest and has the path removed for the site URL like // any other standard URL. auto instance1 = SiteInstanceImpl::CreateForTesting(context(), kGuestUrl); EXPECT_FALSE(instance1->IsGuest()); if (AreDefaultSiteInstancesEnabled()) { EXPECT_TRUE(instance1->IsDefaultSiteInstance()); } else { EXPECT_NE(kGuestUrl, instance1->GetSiteURL()); EXPECT_EQ(GURL(std::string(kGuestScheme) + "://abc123/"), instance1->GetSiteURL()); } // Verify that a SiteInstance created with CreateForGuest() is considered // a <webview> guest and has a site URL that reflects the guest's // StoragePartition configuration. const StoragePartitionConfig kGuestConfig = StoragePartitionConfig::Create( context(), "appid", "partition_name", /*in_memory=*/false); const GURL kGuestSiteUrl(std::string(kGuestScheme) + "://appid/persist?partition_name#nofallback"); auto instance2 = SiteInstanceImpl::CreateForGuest(context(), kGuestConfig); EXPECT_TRUE(instance2->IsGuest()); EXPECT_EQ(kGuestSiteUrl, instance2->GetSiteURL()); } TEST_F(SiteInstanceTest, DoesSiteRequireDedicatedProcess) { class CustomBrowserClient : public EffectiveURLContentBrowserClient { public: CustomBrowserClient(const GURL& url_to_modify, const GURL& url_to_return, bool requires_dedicated_process, const std::string& additional_webui_scheme) : EffectiveURLContentBrowserClient(url_to_modify, url_to_return, requires_dedicated_process), additional_webui_scheme_(additional_webui_scheme) { DCHECK(!additional_webui_scheme.empty()); } private: void GetAdditionalWebUISchemes( std::vector<std::string>* additional_schemes) override { additional_schemes->push_back(additional_webui_scheme_); } const std::string additional_webui_scheme_; }; const std::vector<std::string> kUrlsThatDoNotRequireADedicatedProcess = { "about:blank", "http://foo.com", "data:text/html,Hello World!", "file:///tmp/test.txt", }; const char* kExplicitlyIsolatedURL = "http://isolated.com"; const char* kCustomWebUIScheme = "my-webui"; const char* kCustomWebUIUrl = "my-webui://show-stats"; const char* kCustomUrl = "http://custom.foo.com"; const char* kCustomAppUrl = "custom-scheme://custom"; const std::vector<std::string> kUrlsThatAlwaysRequireADedicatedProcess = { kExplicitlyIsolatedURL, kUnreachableWebDataURL, GetWebUIURLString("network-error"), kCustomUrl, kCustomAppUrl, kCustomWebUIUrl, }; CustomBrowserClient modified_client(GURL(kCustomUrl), GURL(kCustomAppUrl), /* requires_dedicated_process */ true, kCustomWebUIScheme); ContentBrowserClient* regular_client = SetBrowserClientForTesting(&modified_client); IsolationContext isolation_context(context()); auto* policy = ChildProcessSecurityPolicyImpl::GetInstance(); policy->AddFutureIsolatedOrigins( {url::Origin::Create(GURL(kExplicitlyIsolatedURL))}, IsolatedOriginSource::TEST); for (const auto& url : kUrlsThatAlwaysRequireADedicatedProcess) { EXPECT_TRUE(DoesURLRequireDedicatedProcess(isolation_context, GURL(url))); } for (const auto& url : kUrlsThatDoNotRequireADedicatedProcess) { EXPECT_EQ(AreAllSitesIsolatedForTesting(), DoesURLRequireDedicatedProcess(isolation_context, GURL(url))); } SetBrowserClientForTesting(regular_client); } TEST_F(SiteInstanceTest, DoWebUIURLsWithSubdomainsUseTLDForProcessLock) { class CustomWebUIWebUIControllerFactory : public WebUIControllerFactory { public: std::unique_ptr<WebUIController> CreateWebUIControllerForURL( WebUI* web_ui, const GURL& url) override { return nullptr; } WebUI::TypeID GetWebUIType(BrowserContext* browser_context, const GURL& url) override { return WebUI::kNoWebUI; } bool UseWebUIForURL(BrowserContext* browser_context, const GURL& url) override { return HasWebUIScheme(url); } }; CustomWebUIWebUIControllerFactory factory; content::ScopedWebUIControllerFactoryRegistration factory_registration( &factory); const GURL webui_tld_url = GetWebUIURL("foo"); const GURL webui_host_bar_url = GetWebUIURL("bar.foo"); const GURL webui_host_baz_url = GetWebUIURL("baz.foo"); const SiteInfo webui_tld_site_info = GetSiteInfoForURL(webui_tld_url); const SiteInfo webui_host_bar_site_info = GetSiteInfoForURL(webui_host_bar_url); const SiteInfo webui_host_baz_site_info = GetSiteInfoForURL(webui_host_baz_url); // WebUI URLs should result in SiteURLs with the full scheme and hostname // of the WebUI URL. EXPECT_EQ(webui_tld_url, webui_tld_site_info.site_url()); EXPECT_EQ(webui_host_bar_url, webui_host_bar_site_info.site_url()); EXPECT_EQ(webui_host_baz_url, webui_host_baz_site_info.site_url()); // WebUI URLs should use their TLD for ProcessLockURLs. EXPECT_EQ(webui_tld_url, webui_tld_site_info.process_lock_url()); EXPECT_EQ(webui_tld_url, webui_host_bar_site_info.process_lock_url()); EXPECT_EQ(webui_tld_url, webui_host_baz_site_info.process_lock_url()); } TEST_F(SiteInstanceTest, ErrorPage) { const GURL non_error_page_url("http://foo.com"); const GURL error_page_url(kUnreachableWebDataURL); // Verify that error SiteInfos are marked by is_error_page() set to true and // are not cross origin isolated. const auto error_site_info = SiteInfo::CreateForErrorPage(CreateStoragePartitionConfigForTesting()); EXPECT_TRUE(error_site_info.is_error_page()); EXPECT_FALSE(error_site_info.web_exposed_isolation_info().is_isolated()); // Verify that non-error URLs don't generate error page SiteInfos. const auto instance = SiteInstanceImpl::CreateForUrlInfo( context(), UrlInfo::CreateForTesting(non_error_page_url)); EXPECT_NE(instance->GetSiteInfo(), error_site_info); // Verify that an error page URL results in error page SiteInfos. const auto error_instance = SiteInstanceImpl::CreateForUrlInfo( context(), UrlInfo::CreateForTesting(error_page_url)); EXPECT_EQ(error_instance->GetSiteInfo(), error_site_info); EXPECT_FALSE(error_instance->IsCrossOriginIsolated()); // Verify that deriving a SiteInfo for an error page URL always returns // an error page SiteInfo. EXPECT_EQ(error_site_info, instance->DeriveSiteInfo( UrlInfo::CreateForTesting(error_page_url))); // Verify GetRelatedSiteInstance() called with an error page URL always // returns an error page SiteInfo. const auto related_instance = instance->GetRelatedSiteInstance(error_page_url); EXPECT_EQ( error_site_info, static_cast<SiteInstanceImpl*>(related_instance.get())->GetSiteInfo()); } TEST_F(SiteInstanceTest, RelatedSitesInheritStoragePartitionConfig) { const GURL test_url("https://example.com"); // Create a UrlInfo for test_url loaded in a special StoragePartition. const auto non_default_partition_config = CreateStoragePartitionConfigForTesting( /*in_memory=*/false, /*partition_domain=*/"test_partition"); const UrlInfo partitioned_url_info( UrlInfoInit(test_url).WithStoragePartitionConfig( non_default_partition_config)); // Create a SiteInstance for test_url in the special StoragePartition, and // verify that the StoragePartition is correct. const auto partitioned_instance = SiteInstanceImpl::CreateForUrlInfo(context(), partitioned_url_info); EXPECT_EQ(non_default_partition_config, static_cast<SiteInstanceImpl*>(partitioned_instance.get()) ->GetSiteInfo() .storage_partition_config()); // Create a related SiteInstance that doesn't specify a // StoragePartitionConfig and make sure the StoragePartition gets propagated. const auto related_instance = partitioned_instance->GetRelatedSiteInstance(test_url); EXPECT_EQ(non_default_partition_config, static_cast<SiteInstanceImpl*>(related_instance.get()) ->GetSiteInfo() .storage_partition_config()); } TEST_F(SiteInstanceTest, GetNonOriginKeyedEquivalentPreservesIsPdf) { auto origin_isolation_request = static_cast<UrlInfo::OriginIsolationRequest>( UrlInfo::OriginIsolationRequest::kOriginAgentCluster | UrlInfo::OriginIsolationRequest::kRequiresOriginKeyedProcess); UrlInfo url_info_pdf_with_oac( UrlInfoInit(GURL("https://foo.com/test.pdf")) .WithOriginIsolationRequest(origin_isolation_request) .WithIsPdf(true)); SiteInfo site_info_pdf_with_origin_key = SiteInfo::Create(IsolationContext(context()), url_info_pdf_with_oac); SiteInfo site_info_pdf_no_origin_key = site_info_pdf_with_origin_key.GetNonOriginKeyedEquivalentForMetrics( IsolationContext(context())); // Verify that the non-origin-keyed equivalent still has the is_pdf flag set // but has the is_origin_keyed flag cleared. EXPECT_TRUE(site_info_pdf_with_origin_key.is_pdf()); EXPECT_TRUE(site_info_pdf_no_origin_key.is_pdf()); EXPECT_TRUE(site_info_pdf_with_origin_key.requires_origin_keyed_process()); EXPECT_FALSE(site_info_pdf_no_origin_key.requires_origin_keyed_process()); } // This test makes sure that if we create a SiteInfo with a UrlInfo where // kOriginAgentCluster is set but kRequiresOriginKeyedProcess is not, that the // resulting SiteInfo does not have `requires_origin_keyed_process_` true. TEST_F(SiteInstanceTest, SiteInfoDetermineProcessLock_OriginAgentCluster) { GURL a_foo_url("https://a.foo.com/"); GURL foo_url("https://foo.com"); // In the test below, it's important for the IsolationContext to have a // non-null BrowsingInstanceId, otherwise the call to // ChildProcessSecurityPolicyImpl::GetMatchingProcessIsolatedOrigin() will // skip over the check for OAC process isolated origins, which is required for // this test to operate. SiteInfo site_info_for_a_foo = SiteInfo::Create( IsolationContext(BrowsingInstanceId::FromUnsafeValue(42), context()), UrlInfo(UrlInfoInit(a_foo_url).WithOriginIsolationRequest( UrlInfo::OriginIsolationRequest::kOriginAgentCluster))); EXPECT_TRUE( SiteIsolationPolicy::IsProcessIsolationForOriginAgentClusterEnabled()); EXPECT_EQ(foo_url, site_info_for_a_foo.process_lock_url()); EXPECT_FALSE(site_info_for_a_foo.requires_origin_keyed_process()); } } // namespace content