blob: 39665a044fee59905c050c7788c78580e69a0ab7 [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2017 The Chromium Authors
alexmos3b9ad102017-05-26 23:41:082// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Lukasz Anforowicz770c0be2018-01-10 15:24:485#include <sstream>
Avi Drissman5d5d48d62022-01-07 20:23:586#include <tuple>
Andrew Stone0a177fe22019-06-26 08:12:047#include <vector>
Lukasz Anforowicz770c0be2018-01-10 15:24:488
alexmos3b9ad102017-05-26 23:41:089#include "base/command_line.h"
Avi Drissmanadac21992023-01-11 23:46:3910#include "base/functional/bind.h"
Keishi Hattori0e45c022021-11-27 09:25:5211#include "base/memory/raw_ptr.h"
Ali Hijazid87307d2022-11-07 20:15:0312#include "base/memory/raw_ref.h"
Marijn Kruisselbrink7a0d5e182018-05-24 22:55:0913#include "base/strings/string_util.h"
Lei Zhange02299a2021-04-26 23:12:2414#include "base/strings/stringprintf.h"
Guido Urdanetaef4e91942020-11-09 15:06:2415#include "base/test/bind.h"
W. James MacLeana485f0e2021-01-29 17:18:0516#include "base/test/metrics/histogram_tester.h"
Jiacheng Guo15d61372025-08-13 06:11:2517#include "base/test/run_until.h"
Lukasz Anforowicz0672f8a2017-11-30 01:07:0618#include "base/test/scoped_feature_list.h"
Marijn Kruisselbrink7a0d5e182018-05-24 22:55:0919#include "build/build_config.h"
Nasko Oskov59562ccf2017-08-25 03:40:0020#include "content/browser/bad_message.h"
alexmos3b9ad102017-05-26 23:41:0821#include "content/browser/child_process_security_policy_impl.h"
W. James MacLean7f76c2202021-11-15 16:27:4922#include "content/browser/origin_agent_cluster_isolation_state.h"
Sharon Yanga005ca12021-11-16 20:09:4223#include "content/browser/process_lock.h"
Charlie Reis9ce0ed222024-09-05 22:05:2624#include "content/browser/process_reuse_policy.h"
W. James MacLeana485f0e2021-01-29 17:18:0525#include "content/browser/renderer_host/navigation_request.h"
danakj10f32372020-09-15 22:25:1626#include "content/browser/renderer_host/navigator.h"
Lukasz Anforowiczc62eede2018-12-28 21:52:0127#include "content/browser/renderer_host/render_process_host_impl.h"
Sharon Yangd70a5392021-10-26 23:06:3228#include "content/browser/site_info.h"
Nasko Oskov59562ccf2017-08-25 03:40:0029#include "content/browser/storage_partition_impl.h"
alexmos3b9ad102017-05-26 23:41:0830#include "content/browser/web_contents/web_contents_impl.h"
Rakina Zata Amnid3af5db92020-08-06 06:51:3931#include "content/common/content_navigation_policy.h"
Alex Moshchuk5345a3f2023-04-03 23:36:3332#include "content/common/features.h"
Aaron Colwellea6921f2019-01-29 16:50:3933#include "content/public/browser/browser_or_resource_context.h"
Lukasz Anforowiczc62eede2018-12-28 21:52:0134#include "content/public/browser/render_frame_host.h"
alexmos3b9ad102017-05-26 23:41:0835#include "content/public/browser/render_process_host.h"
Lukasz Anforowicz87273492018-12-04 22:28:5736#include "content/public/browser/site_isolation_policy.h"
Robbie McElrath7d4bd852021-07-24 04:02:1937#include "content/public/browser/storage_partition_config.h"
Robbie McElratheae661e2023-08-10 19:05:2838#include "content/public/browser/web_exposed_isolation_level.h"
Alex Moshchuke456cf552020-08-19 17:09:2939#include "content/public/common/content_client.h"
Lukasz Anforowicz0672f8a2017-11-30 01:07:0640#include "content/public/common/content_features.h"
alexmos3b9ad102017-05-26 23:41:0841#include "content/public/common/content_switches.h"
Sreeja Kamishettyce8d5942020-08-19 11:25:5142#include "content/public/test/back_forward_cache_util.h"
Peter Kasting919ce652020-05-07 10:22:3643#include "content/public/test/browser_test.h"
alexmos3b9ad102017-05-26 23:41:0844#include "content/public/test/browser_test_utils.h"
45#include "content/public/test/content_browser_test.h"
46#include "content/public/test/content_browser_test_utils.h"
Arthur Sonzogni1dca7cf2021-08-06 07:49:4947#include "content/public/test/content_mock_cert_verifier.h"
Nasko Oskovd83b5712018-05-04 04:50:5748#include "content/public/test/navigation_handle_observer.h"
W. James MacLeanc6dc86c2021-08-12 13:58:3749#include "content/public/test/prerender_test_util.h"
alexmos3b9ad102017-05-26 23:41:0850#include "content/public/test/test_frame_navigation_observer.h"
51#include "content/public/test/test_navigation_observer.h"
52#include "content/public/test/test_utils.h"
W. James MacLean222a2472020-08-14 22:00:2253#include "content/public/test/url_loader_interceptor.h"
alexmos3b9ad102017-05-26 23:41:0854#include "content/shell/browser/shell.h"
55#include "content/test/content_browser_test_utils_internal.h"
W. James MacLeanb70fab82020-05-01 18:51:1456#include "content/test/did_commit_navigation_interceptor.h"
Mario Sanchez Prada2590ec6d2019-08-14 17:17:0557#include "mojo/public/cpp/bindings/pending_receiver.h"
Julie Jeongeun Kimaf5fcae42019-11-28 01:29:2158#include "mojo/public/cpp/bindings/receiver_set.h"
alexmos3b9ad102017-05-26 23:41:0859#include "net/dns/mock_host_resolver.h"
60#include "net/test/embedded_test_server/embedded_test_server.h"
W. James MacLean64ddbcc2020-01-24 22:34:2261#include "net/test/embedded_test_server/http_request.h"
62#include "net/test/embedded_test_server/http_response.h"
Marijn Kruisselbrink7a0d5e182018-05-24 22:55:0963#include "services/network/public/cpp/features.h"
W. James MacLeana485f0e2021-01-29 17:18:0564#include "testing/gmock/include/gmock/gmock.h"
65#include "testing/gtest/include/gtest/gtest.h"
Marijn Kruisselbrinkcde64632018-06-22 22:45:1666#include "third_party/blink/public/common/features.h"
Ari Chivukulab54d9042021-09-21 00:27:1467#include "third_party/blink/public/common/storage_key/storage_key.h"
Ari Chivukulaccb16aeb2021-10-01 01:47:1268#include "third_party/blink/public/common/tokens/tokens.h"
Ken Rockot8b8424552020-02-20 06:12:4169#include "third_party/blink/public/mojom/dom_storage/dom_storage.mojom-test-utils.h"
alexmos3b9ad102017-05-26 23:41:0870#include "url/gurl.h"
71
72namespace content {
73
Alex Moshchukc4679422019-06-11 17:04:4874using IsolatedOriginSource = ChildProcessSecurityPolicy::IsolatedOriginSource;
75
Alex Moshchuk99b795422019-03-07 00:27:3276// This is a base class for all tests in this class. It does not isolate any
77// origins and only provides common helper functions to the other test classes.
78class IsolatedOriginTestBase : public ContentBrowserTest {
79 public:
Sharon Yang034fbb722021-06-23 17:21:3280 IsolatedOriginTestBase() = default;
81 ~IsolatedOriginTestBase() override = default;
82
83 IsolatedOriginTestBase(const IsolatedOriginTestBase&) = delete;
84 IsolatedOriginTestBase& operator=(const IsolatedOriginTestBase&) = delete;
Alex Moshchuk8e5c1952019-01-15 03:39:5085
Ari Chivukula5350aad92021-08-10 02:42:2486 // Check if `origin` is an isolated origin. This helper is used in tests
Alex Moshchuk99b795422019-03-07 00:27:3287 // that care only about globally applicable isolated origins (not restricted
88 // to a particular BrowsingInstance or profile).
89 bool IsIsolatedOrigin(const url::Origin& origin) {
90 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
91 IsolationContext isolation_context(
92 shell()->web_contents()->GetBrowserContext());
W. James MacLean46cf26212020-10-01 16:43:3793 return policy->IsIsolatedOrigin(isolation_context, origin,
94 false /* origin_requests_isolation */);
Alex Moshchuk99b795422019-03-07 00:27:3295 }
Alex Moshchuk8e5c1952019-01-15 03:39:5096
Alex Moshchuk99b795422019-03-07 00:27:3297 bool IsIsolatedOrigin(const GURL& url) {
98 return IsIsolatedOrigin(url::Origin::Create(url));
99 }
Alex Moshchuk8e5c1952019-01-15 03:39:50100
W. James MacLean7f76c2202021-11-15 16:27:49101 OriginAgentClusterIsolationState MakeOACIsolationState(
102 bool requires_origin_keyed_process) {
103 // Assume |requires_origin_keyed_process| is the same as
104 // |is_origin_agent_cluster| here.
105 if (!requires_origin_keyed_process) {
Camille Lamy5ce9b962025-08-08 12:10:45106 return OriginAgentClusterIsolationState::CreateNonIsolatedByDefault();
W. James MacLean7f76c2202021-11-15 16:27:49107 }
108 return OriginAgentClusterIsolationState::CreateForOriginAgentCluster(
Camille Lamy5ce9b962025-08-08 12:10:45109 /*had_oac_request=*/true, requires_origin_keyed_process);
W. James MacLean7f76c2202021-11-15 16:27:49110 }
111
112 bool ShouldOriginGetOptInProcessIsolation(const url::Origin& origin) {
Domenic Denicola7dbd3d12021-01-08 21:26:56113 auto* site_instance = static_cast<SiteInstanceImpl*>(
Dave Tapuska327c06c92022-06-13 20:31:51114 shell()->web_contents()->GetPrimaryMainFrame()->GetSiteInstance());
Domenic Denicola7dbd3d12021-01-08 21:26:56115
W. James MacLean7f76c2202021-11-15 16:27:49116 OriginAgentClusterIsolationState isolation_request =
Camille Lamy5ce9b962025-08-08 12:10:45117 OriginAgentClusterIsolationState::CreateNonIsolatedByDefault();
W. James MacLean7f76c2202021-11-15 16:27:49118
Domenic Denicola7dbd3d12021-01-08 21:26:56119 return ChildProcessSecurityPolicyImpl::GetInstance()
W. James MacLeand42fa812021-11-18 22:59:26120 ->DetermineOriginAgentClusterIsolation(
121 site_instance->GetIsolationContext(), origin, isolation_request)
W. James MacLean7f76c2202021-11-15 16:27:49122 .requires_origin_keyed_process();
Domenic Denicola7dbd3d12021-01-08 21:26:56123 }
124
W. James MacLeanc07dc41b2022-07-25 18:52:16125 // Assuming no additional explicit opt-in or opt-out was requested, check what
126 // isolation state would currently be used for a navigation to |url| in
127 // |site_instance| in the test, based on the current state in the
128 // BrowsingInstance.
W. James MacLeanae8486cb2023-05-24 15:57:13129 static OriginAgentClusterIsolationState DetermineOriginAgentClusterIsolation(
W. James MacLean64448dea2022-03-18 20:22:21130 SiteInstanceImpl* site_instance,
131 const GURL& url) {
132 OriginAgentClusterIsolationState isolation_request =
W. James MacLeane66843c2023-04-26 19:15:57133 site_instance->GetIsolationContext().default_isolation_state();
W. James MacLean64448dea2022-03-18 20:22:21134
135 return ChildProcessSecurityPolicyImpl::GetInstance()
136 ->DetermineOriginAgentClusterIsolation(
137 site_instance->GetIsolationContext(), url::Origin::Create(url),
138 isolation_request);
139 }
140
W. James MacLeane84fa112020-07-14 17:25:54141 ProcessLock ProcessLockFromUrl(const std::string& url) {
Robbie McElrath7d4bd852021-07-24 04:02:19142 BrowserContext* browser_context = web_contents()->GetBrowserContext();
Sharon Yang2c077a72021-11-30 02:27:58143 return ProcessLock::FromSiteInfo(SiteInfo(
Camille Lamy52a51202025-07-29 14:16:12144 AgentClusterKey::CreateSiteKeyed(GURL(url)),
Robbie McElratheae661e2023-08-10 19:05:28145 /*site_url=*/GURL(url),
146 /*process_lock_url=*/GURL(url),
W. James MacLean2a84fbf2023-05-12 18:13:43147 /*requires_origin_keyed_process=*/false,
Camille Lamy5ce9b962025-08-08 12:10:45148 AgentClusterKey::OACStatus::kSiteKeyedByDefault,
W. James MacLean2a84fbf2023-05-12 18:13:43149 /*is_sandboxed=*/false, UrlInfo::kInvalidUniqueSandboxId,
Robbie McElrath7d4bd852021-07-24 04:02:19150 StoragePartitionConfig::CreateDefault(browser_context),
Robbie McElratheae661e2023-08-10 19:05:28151 WebExposedIsolationInfo::CreateNonIsolated(),
152 WebExposedIsolationLevel::kNotIsolated, /*is_guest=*/false,
W. James MacLean2a84fbf2023-05-12 18:13:43153 /*does_site_request_dedicated_process_for_coop=*/false,
Ellyc737a6302024-08-19 15:30:51154 /*is_jit_disabled=*/false, /*are_v8_optimizations_disabled=*/false,
Camille Lamy52a51202025-07-29 14:16:12155 /*is_pdf=*/false, /*is_fenced=*/false));
W. James MacLeane84fa112020-07-14 17:25:54156 }
157
W. James MacLeanf79c97e2019-05-02 20:35:46158 WebContentsImpl* web_contents() const {
159 return static_cast<WebContentsImpl*>(shell()->web_contents());
160 }
161
W. James MacLeane84fa112020-07-14 17:25:54162 // Helper function that computes an appropriate process lock that corresponds
Ari Chivukula5350aad92021-08-10 02:42:24163 // to `url`'s origin (without converting to sites, handling effective URLs,
Aaron Colwell80f85bb2020-05-19 01:55:06164 // etc). This must be equivalent to what
165 // SiteInstanceImpl::DetermineProcessLockURL() would return
166 // for strict origin isolation.
W. James MacLean222a2472020-08-14 22:00:22167 // Note: do not use this for opt-in origin isolation, as it won't set
W. James MacLean7f76c2202021-11-15 16:27:49168 // requires_origin_keyed_process to true.
Camille Lamy52a51202025-07-29 14:16:12169 // TODO(crbug.com/433443082): Update this helper to create origin-keyed
170 // AgentClusterKeys once origin-keyed AgentClusterKeys are created in contexts
171 // other than when requires_origin_keyed_process is true.
W. James MacLeane84fa112020-07-14 17:25:54172 ProcessLock GetStrictProcessLock(const GURL& url) {
Robbie McElrath7d4bd852021-07-24 04:02:19173 BrowserContext* browser_context = web_contents()->GetBrowserContext();
W. James MacLeane84fa112020-07-14 17:25:54174 GURL origin_url = url::Origin::Create(url).GetURL();
Sharon Yang2c077a72021-11-30 02:27:58175 return ProcessLock::FromSiteInfo(SiteInfo(
Camille Lamy52a51202025-07-29 14:16:12176 AgentClusterKey::CreateSiteKeyed(origin_url),
Robbie McElratheae661e2023-08-10 19:05:28177 /*site_url=*/origin_url,
178 /*process_lock_url=*/origin_url,
W. James MacLean2a84fbf2023-05-12 18:13:43179 /*requires_origin_keyed_process=*/false,
Camille Lamy5ce9b962025-08-08 12:10:45180 AgentClusterKey::OACStatus::kSiteKeyedByDefault,
W. James MacLean2a84fbf2023-05-12 18:13:43181 /*is_sandboxed=*/false, UrlInfo::kInvalidUniqueSandboxId,
Robbie McElrath7d4bd852021-07-24 04:02:19182 StoragePartitionConfig::CreateDefault(browser_context),
Robbie McElratheae661e2023-08-10 19:05:28183 WebExposedIsolationInfo::CreateNonIsolated(),
184 WebExposedIsolationLevel::kNotIsolated, /*is_guest=*/false,
W. James MacLean2a84fbf2023-05-12 18:13:43185 /*does_site_request_dedicated_process_for_coop=*/false,
Ellyc737a6302024-08-19 15:30:51186 /*is_jit_disabled=*/false, /*are_v8_optimizations_disabled=*/false,
Camille Lamy52a51202025-07-29 14:16:12187 /*is_pdf=*/false, /*is_fenced=*/false));
Aaron Colwell80f85bb2020-05-19 01:55:06188 }
Arthur Sonzogni1dca7cf2021-08-06 07:49:49189
190 protected:
191 void SetUpOnMainThread() override {
192 ContentBrowserTest::SetUpOnMainThread();
193 mock_cert_verifier_.mock_cert_verifier()->set_default_result(net::OK);
194 }
195
196 void SetUpCommandLine(base::CommandLine* command_line) override {
Arthur Sonzogni1dca7cf2021-08-06 07:49:49197 mock_cert_verifier_.SetUpCommandLine(command_line);
198 }
199
200 void SetUpInProcessBrowserTestFixture() override {
201 ContentBrowserTest::SetUpInProcessBrowserTestFixture();
202 mock_cert_verifier_.SetUpInProcessBrowserTestFixture();
203 }
204
205 void TearDownInProcessBrowserTestFixture() override {
206 ContentBrowserTest::TearDownInProcessBrowserTestFixture();
207 mock_cert_verifier_.TearDownInProcessBrowserTestFixture();
208 }
209
210 private:
211 content::ContentMockCertVerifier mock_cert_verifier_;
Alex Moshchuk99b795422019-03-07 00:27:32212};
Alex Moshchuk8e5c1952019-01-15 03:39:50213
Alex Moshchuk99b795422019-03-07 00:27:32214class IsolatedOriginTest : public IsolatedOriginTestBase {
alexmos3b9ad102017-05-26 23:41:08215 public:
Jiacheng Guo15d61372025-08-13 06:11:25216 IsolatedOriginTest() = default;
Sharon Yang034fbb722021-06-23 17:21:32217 ~IsolatedOriginTest() override = default;
218
219 IsolatedOriginTest(const IsolatedOriginTest&) = delete;
220 IsolatedOriginTest& operator=(const IsolatedOriginTest&) = delete;
alexmos3b9ad102017-05-26 23:41:08221
222 void SetUpCommandLine(base::CommandLine* command_line) override {
Arthur Sonzogni1dca7cf2021-08-06 07:49:49223 IsolatedOriginTestBase::SetUpCommandLine(command_line);
alexmos3b9ad102017-05-26 23:41:08224 ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
225
226 std::string origin_list =
227 embedded_test_server()->GetURL("isolated.foo.com", "/").spec() + "," +
228 embedded_test_server()->GetURL("isolated.bar.com", "/").spec();
229 command_line->AppendSwitchASCII(switches::kIsolateOrigins, origin_list);
230 }
231
232 void SetUpOnMainThread() override {
Arthur Sonzogni1dca7cf2021-08-06 07:49:49233 IsolatedOriginTestBase::SetUpOnMainThread();
alexmos3b9ad102017-05-26 23:41:08234 host_resolver()->AddRule("*", "127.0.0.1");
235 embedded_test_server()->StartAcceptingConnections();
236 }
237
Alex Moshchuk226d2c902017-11-02 21:43:13238 void InjectAndClickLinkTo(GURL url) {
Avi Drissmanc91bd8e2021-04-19 23:58:44239 EXPECT_TRUE(ExecJs(web_contents(),
240 "var link = document.createElement('a');"
241 "link.href = '" +
242 url.spec() +
243 "';"
244 "document.body.appendChild(link);"
245 "link.click();"));
Alex Moshchuk226d2c902017-11-02 21:43:13246 }
alexmos3b9ad102017-05-26 23:41:08247};
248
Domenic Denicola7dbd3d12021-01-08 21:26:56249// Tests that verify the header can be used to opt-in to origin isolation.
250class OriginIsolationOptInHeaderTest : public IsolatedOriginTestBase {
W. James MacLean64ddbcc2020-01-24 22:34:22251 public:
Domenic Denicola7dbd3d12021-01-08 21:26:56252 OriginIsolationOptInHeaderTest()
W. James MacLeanc6dc86c2021-08-12 13:58:37253 : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
Daniel Vogelheim4e969ce2023-01-13 13:18:27254 feature_list_.InitWithFeatures(
W. James MacLeanf40fcca2024-05-30 17:23:58255 /*enabled_features=*/{features::kOriginIsolationHeader},
256 /*disabled_features=*/{
257 // TODO(https://crbug.com/40259221): update this test to be
258 // parameterized on kOriginKeyedProcessesByDefault, and then
259 // make sure all the tests have correct expectations both with and
260 // without. This will assist in removing the
261 // kOriginAgentClusterDefaultEnabled flag.
262 blink::features::kOriginAgentClusterDefaultEnabled,
263 features::kOriginKeyedProcessesByDefault});
W. James MacLeanc6dc86c2021-08-12 13:58:37264 }
Domenic Denicola7dbd3d12021-01-08 21:26:56265 ~OriginIsolationOptInHeaderTest() override = default;
W. James MacLean64ddbcc2020-01-24 22:34:22266
Sharon Yang034fbb722021-06-23 17:21:32267 OriginIsolationOptInHeaderTest(const OriginIsolationOptInHeaderTest&) =
268 delete;
269 OriginIsolationOptInHeaderTest& operator=(
270 const OriginIsolationOptInHeaderTest&) = delete;
271
W. James MacLean64ddbcc2020-01-24 22:34:22272 void SetUpCommandLine(base::CommandLine* command_line) override {
273 IsolatedOriginTestBase::SetUpCommandLine(command_line);
274 ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
275
276 // This is needed for this test to run properly on platforms where
277 // --site-per-process isn't the default, such as Android.
278 IsolateAllSitesForTesting(command_line);
Domenic Denicola7dbd3d12021-01-08 21:26:56279
W. James MacLean254a08762020-08-12 14:09:37280 // Start the HTTPS server here so derived tests can use it if they override
281 // SetUpCommandLine().
W. James MacLean64ddbcc2020-01-24 22:34:22282 https_server()->AddDefaultHandlers(GetTestDataFilePath());
Domenic Denicola14103152020-04-03 19:40:45283 https_server()->RegisterRequestHandler(
Domenic Denicola7dbd3d12021-01-08 21:26:56284 base::BindRepeating(&OriginIsolationOptInHeaderTest::HandleResponse,
Domenic Denicola14103152020-04-03 19:40:45285 base::Unretained(this)));
Marijn Kruisselbrink1fee8062020-05-08 00:54:44286 ASSERT_TRUE(https_server()->Start());
W. James MacLean254a08762020-08-12 14:09:37287 }
288
Domenic Denicola7dbd3d12021-01-08 21:26:56289 void SetHeaderValue(const std::string& header_value) {
290 header_ = header_value;
291 }
292
W. James MacLeanbae2bd382022-06-13 14:53:40293 void SetRedirectTarget(const std::string& redirect_target) {
294 redirect_target_ = redirect_target;
295 }
296
Domenic Denicola7dbd3d12021-01-08 21:26:56297 // Allows specifying what content to return when an opt-in isolation header is
298 // intercepted. Uses a queue so that multiple requests can be handled without
299 // returning to the test body. If the queue is empty, the document content is
300 // simply "isolate me!".
301 void AddContentToQueue(const std::string& content_str) {
302 content_.push(content_str);
303 }
304
W. James MacLean254a08762020-08-12 14:09:37305 void SetUpOnMainThread() override {
306 IsolatedOriginTestBase::SetUpOnMainThread();
307
W. James MacLean64ddbcc2020-01-24 22:34:22308 host_resolver()->AddRule("*", "127.0.0.1");
309 embedded_test_server()->StartAcceptingConnections();
310 }
311
312 void TearDownOnMainThread() override {
313 ASSERT_TRUE(https_server()->ShutdownAndWaitUntilComplete());
314 IsolatedOriginTestBase::TearDownOnMainThread();
315 }
316
Domenic Denicola7dbd3d12021-01-08 21:26:56317 // Need an https server because the header requires HTTPS.
W. James MacLean64ddbcc2020-01-24 22:34:22318 net::EmbeddedTestServer* https_server() { return &https_server_; }
319
Domenic Denicola14103152020-04-03 19:40:45320 private:
Domenic Denicola14103152020-04-03 19:40:45321 std::unique_ptr<net::test_server::HttpResponse> HandleResponse(
Domenic Denicola7dbd3d12021-01-08 21:26:56322 const net::test_server::HttpRequest& request) {
W. James MacLean1c40862c2020-04-27 21:05:57323 if (request.relative_url == "/isolate_origin") {
Domenic Denicola14103152020-04-03 19:40:45324 auto response = std::make_unique<net::test_server::BasicHttpResponse>();
325 response->set_code(net::HTTP_OK);
326 response->set_content_type("text/html");
327
328 if (header_) {
Domenic Denicola942aca82020-12-12 06:51:59329 response->AddCustomHeader("Origin-Agent-Cluster", *header_);
Domenic Denicola14103152020-04-03 19:40:45330 }
331
Domenic Denicola7dbd3d12021-01-08 21:26:56332 if (!content_.empty()) {
333 response->set_content(content_.front());
334 content_.pop();
335 } else {
336 response->set_content("isolate me!");
337 }
Domenic Denicola14103152020-04-03 19:40:45338 return std::move(response);
W. James MacLeanbae2bd382022-06-13 14:53:40339 } else if (request.relative_url == "/redirect_me") {
340 auto response = std::make_unique<net::test_server::BasicHttpResponse>();
341 response->set_code(net::HTTP_MOVED_PERMANENTLY);
342 response->AddCustomHeader("Location", *redirect_target_);
343 response->AddCustomHeader("Origin-Agent-Cluster", *header_);
344 response->set_content("redirected");
345 return std::move(response);
Domenic Denicola14103152020-04-03 19:40:45346 }
347
348 // If we return nullptr, then the server will go ahead and actually serve
349 // the file.
350 return nullptr;
351 }
352
Domenic Denicola7dbd3d12021-01-08 21:26:56353 net::EmbeddedTestServer https_server_;
354 base::test::ScopedFeatureList feature_list_;
355
Arthur Sonzognic686e8f2024-01-11 08:36:37356 std::optional<std::string> header_;
357 std::optional<std::string> redirect_target_;
Domenic Denicola7dbd3d12021-01-08 21:26:56358 std::queue<std::string> content_;
Domenic Denicola14103152020-04-03 19:40:45359};
360
W. James MacLean64448dea2022-03-18 20:22:21361// A set of tests that enable OriginAgentCluster by default.
362class OriginIsolationDefaultOACTest : public OriginIsolationOptInHeaderTest {
363 public:
364 OriginIsolationDefaultOACTest() {
365 feature_list_.InitAndEnableFeature(
366 blink::features::kOriginAgentClusterDefaultEnabled);
367 }
368
369 ~OriginIsolationDefaultOACTest() override = default;
370
371 OriginIsolationDefaultOACTest(const OriginIsolationDefaultOACTest&) = delete;
372 OriginIsolationDefaultOACTest& operator=(OriginIsolationDefaultOACTest&) =
373 delete;
374
375 private:
376 base::test::ScopedFeatureList feature_list_;
377};
378
W. James MacLean53e24b72023-05-09 20:57:07379// A set of tests that enable process-isolated OriginAgentCluster-by-default.
380class OriginKeyedProcessByDefaultTest : public OriginIsolationOptInHeaderTest {
381 public:
382 OriginKeyedProcessByDefaultTest() {
383 feature_list_.InitWithFeatures(
384 {blink::features::kOriginAgentClusterDefaultEnabled,
385 features::kOriginKeyedProcessesByDefault},
386 {});
387 }
388
389 ~OriginKeyedProcessByDefaultTest() override = default;
390
391 OriginKeyedProcessByDefaultTest(const OriginKeyedProcessByDefaultTest&) =
392 delete;
393 OriginKeyedProcessByDefaultTest& operator=(OriginKeyedProcessByDefaultTest&) =
394 delete;
395
W. James MacLeanae8486cb2023-05-24 15:57:13396 void SetUpOnMainThread() override {
397 OriginIsolationOptInHeaderTest::SetUpOnMainThread();
398 // Constructing a new BrowserClient also installs it; the old BrowserClient
399 // is restored when the new one destructs.
400 browser_client_ =
401 std::make_unique<OriginAgentClusterByDefaultContentBrowserClient>();
402 }
403
404 protected:
405 // A custom ContentBrowserClient to allow tests to simulate turning off
406 // OriginAgentClusterByDefault.
407 class OriginAgentClusterByDefaultContentBrowserClient
408 : public ContentBrowserTestContentBrowserClient {
409 public:
410 bool ShouldDisableOriginAgentClusterDefault(
411 BrowserContext* browser_context) override {
412 return should_disable_origin_agent_cluster_default_;
413 }
414
415 void SetShouldDisableOriginAgentClusterDefault(bool should_disable) {
416 should_disable_origin_agent_cluster_default_ = should_disable;
417 }
418
419 private:
420 bool should_disable_origin_agent_cluster_default_ = false;
421 };
422
423 std::unique_ptr<OriginAgentClusterByDefaultContentBrowserClient>
424 browser_client_;
425
W. James MacLean53e24b72023-05-09 20:57:07426 private:
427 base::test::ScopedFeatureList feature_list_;
428};
429
W. James MacLeanc6dc86c2021-08-12 13:58:37430class OriginIsolationPrerenderOptInHeaderTest
431 : public OriginIsolationOptInHeaderTest {
432 public:
433 OriginIsolationPrerenderOptInHeaderTest()
434 : prerender_helper_(base::BindRepeating(
435 &OriginIsolationPrerenderOptInHeaderTest::prerender_web_contents,
436 base::Unretained(this))) {}
437 ~OriginIsolationPrerenderOptInHeaderTest() override = default;
438
439 OriginIsolationPrerenderOptInHeaderTest(
440 const OriginIsolationPrerenderOptInHeaderTest&) = delete;
441 OriginIsolationPrerenderOptInHeaderTest& operator=(
442 const OriginIsolationPrerenderOptInHeaderTest&) = delete;
443
444 void SetUpCommandLine(base::CommandLine* command_line) override {
445 // This must be called prior to starting the test server.
Robert Lineb2a99c2023-08-24 02:42:26446 prerender_helper_.RegisterServerRequestMonitor(https_server());
W. James MacLeanc6dc86c2021-08-12 13:58:37447 OriginIsolationOptInHeaderTest::SetUpCommandLine(command_line);
448 }
449
450 void set_prerender_web_contents(WebContents* web_contents) {
Jagadesh P1c8699c22023-11-13 14:09:37451 prerender_web_contents_ = web_contents->GetWeakPtr();
W. James MacLeanc6dc86c2021-08-12 13:58:37452 }
Jagadesh P1c8699c22023-11-13 14:09:37453 WebContents* prerender_web_contents() {
454 CHECK(prerender_web_contents_);
455 return prerender_web_contents_.get();
456 }
W. James MacLeanc6dc86c2021-08-12 13:58:37457
458 protected:
459 test::PrerenderTestHelper prerender_helper_;
460
461 private:
Jagadesh P1c8699c22023-11-13 14:09:37462 base::WeakPtr<WebContents> prerender_web_contents_;
W. James MacLeanc6dc86c2021-08-12 13:58:37463}; // class OriginIsolationPrerenderOptInHeaderTest
464
465// As in OriginIsolationOptInHeaderTest, but with same-process origin
466// isolation.
W. James MacLean92e39c82021-02-25 23:27:34467class SameProcessOriginIsolationOptInHeaderTest
468 : public OriginIsolationOptInHeaderTest {
469 public:
470 SameProcessOriginIsolationOptInHeaderTest() = default;
471 ~SameProcessOriginIsolationOptInHeaderTest() override = default;
472
Sharon Yang034fbb722021-06-23 17:21:32473 SameProcessOriginIsolationOptInHeaderTest(
474 const SameProcessOriginIsolationOptInHeaderTest&) = delete;
475 SameProcessOriginIsolationOptInHeaderTest& operator=(
476 const SameProcessOriginIsolationOptInHeaderTest&) = delete;
477
W. James MacLean92e39c82021-02-25 23:27:34478 void SetUpCommandLine(base::CommandLine* command_line) override {
479 OriginIsolationOptInHeaderTest::SetUpCommandLine(command_line);
480 command_line->AppendSwitch(switches::kDisableSiteIsolation);
481 command_line->RemoveSwitch(switches::kSitePerProcess);
482 }
W. James MacLean92e39c82021-02-25 23:27:34483};
484
W. James MacLean40304612021-08-25 15:25:58485// As in SameProcessOriginIsolationOptInHeaderTest, but command-line isolate
486// foo.com.
487class SameProcessOriginIsolationOptInHeaderWithIsolatedOriginTest
488 : public SameProcessOriginIsolationOptInHeaderTest {
489 public:
490 SameProcessOriginIsolationOptInHeaderWithIsolatedOriginTest() = default;
491 ~SameProcessOriginIsolationOptInHeaderWithIsolatedOriginTest() override =
492 default;
493
494 SameProcessOriginIsolationOptInHeaderWithIsolatedOriginTest(
495 const SameProcessOriginIsolationOptInHeaderWithIsolatedOriginTest&) =
496 delete;
497 SameProcessOriginIsolationOptInHeaderWithIsolatedOriginTest& operator=(
498 const SameProcessOriginIsolationOptInHeaderWithIsolatedOriginTest&) =
499 delete;
500
501 void SetUpCommandLine(base::CommandLine* command_line) override {
502 SameProcessOriginIsolationOptInHeaderTest::SetUpCommandLine(command_line);
503 command_line->AppendSwitchASCII(switches::kIsolateOrigins,
504 "https://foo.com/");
505 }
506};
507
W. James MacLean4f51ce22021-03-22 22:19:04508// Force WebSecurity off for tests.
509class SameProcessNoWebSecurityOriginIsolationOptInHeaderTest
510 : public SameProcessOriginIsolationOptInHeaderTest {
511 public:
512 SameProcessNoWebSecurityOriginIsolationOptInHeaderTest() = default;
513 ~SameProcessNoWebSecurityOriginIsolationOptInHeaderTest() override = default;
514
515 // Disallow copy & assign.
516 SameProcessNoWebSecurityOriginIsolationOptInHeaderTest(
517 const SameProcessNoWebSecurityOriginIsolationOptInHeaderTest&) = delete;
518 SameProcessNoWebSecurityOriginIsolationOptInHeaderTest& operator=(
519 const SameProcessNoWebSecurityOriginIsolationOptInHeaderTest&) = delete;
520
521 void SetUpCommandLine(base::CommandLine* command_line) override {
522 SameProcessOriginIsolationOptInHeaderTest::SetUpCommandLine(command_line);
523 command_line->AppendSwitch(switches::kDisableWebSecurity);
524 }
525};
526
Domenic Denicola4d1145d2020-10-29 15:05:33527// Used for a few tests that check non-HTTPS secure context behavior.
528class OriginIsolationOptInHttpServerHeaderTest : public IsolatedOriginTestBase {
529 public:
530 OriginIsolationOptInHttpServerHeaderTest() = default;
531
Sharon Yang034fbb722021-06-23 17:21:32532 OriginIsolationOptInHttpServerHeaderTest(
533 const OriginIsolationOptInHttpServerHeaderTest&) = delete;
534 OriginIsolationOptInHttpServerHeaderTest& operator=(
535 const OriginIsolationOptInHttpServerHeaderTest&) = delete;
536
Domenic Denicola4d1145d2020-10-29 15:05:33537 void SetUpCommandLine(base::CommandLine* command_line) override {
538 IsolatedOriginTestBase::SetUpCommandLine(command_line);
539 ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
540
541 // This is needed for this test to run properly on platforms where
542 // --site-per-process isn't the default, such as Android.
543 IsolateAllSitesForTesting(command_line);
544
545 feature_list_.InitAndEnableFeature(features::kOriginIsolationHeader);
546
547 embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
548 &OriginIsolationOptInHttpServerHeaderTest::HandleResponse,
549 base::Unretained(this)));
550 }
551
552 void SetUpOnMainThread() override {
553 host_resolver()->AddRule("*", "127.0.0.1");
554 embedded_test_server()->StartAcceptingConnections();
555 }
556
Domenic Denicola4d1145d2020-10-29 15:05:33557 private:
558 std::unique_ptr<net::test_server::HttpResponse> HandleResponse(
559 const net::test_server::HttpRequest& request) {
560 auto response = std::make_unique<net::test_server::BasicHttpResponse>();
561 response->set_code(net::HTTP_OK);
562 response->set_content_type("text/html");
Domenic Denicola942aca82020-12-12 06:51:59563 response->AddCustomHeader("Origin-Agent-Cluster", "?1");
Domenic Denicola4d1145d2020-10-29 15:05:33564
565 response->set_content("isolate me!");
566 return std::move(response);
567 }
568
569 base::test::ScopedFeatureList feature_list_;
Domenic Denicola4d1145d2020-10-29 15:05:33570};
571
W. James MacLean254a08762020-08-12 14:09:37572// This class allows testing the interaction of OptIn isolation and command-line
573// isolation for origins. Tests using this class will isolate foo.com and
574// bar.com by default using command-line isolation, but any opt-in isolation
575// will override this.
Domenic Denicola7dbd3d12021-01-08 21:26:56576class OriginIsolationOptInHeaderCommandLineTest
577 : public OriginIsolationOptInHeaderTest {
W. James MacLean254a08762020-08-12 14:09:37578 public:
Domenic Denicola7dbd3d12021-01-08 21:26:56579 OriginIsolationOptInHeaderCommandLineTest() = default;
W. James MacLean254a08762020-08-12 14:09:37580
Sharon Yang034fbb722021-06-23 17:21:32581 OriginIsolationOptInHeaderCommandLineTest(
582 const OriginIsolationOptInHeaderCommandLineTest&) = delete;
583 OriginIsolationOptInHeaderCommandLineTest& operator=(
584 const OriginIsolationOptInHeaderCommandLineTest&) = delete;
585
W. James MacLean254a08762020-08-12 14:09:37586 void SetUpCommandLine(base::CommandLine* command_line) override {
Domenic Denicola7dbd3d12021-01-08 21:26:56587 OriginIsolationOptInHeaderTest::SetUpCommandLine(command_line);
W. James MacLean254a08762020-08-12 14:09:37588 // The base class should already have started the HTTPS server so we can use
589 // it here to generate origins to specify on the command line.
590 ASSERT_TRUE(https_server()->Started());
591
592 std::string origin_list = https_server()->GetURL("foo.com", "/").spec() +
593 "," +
594 https_server()->GetURL("bar.com", "/").spec();
595 command_line->AppendSwitchASCII(switches::kIsolateOrigins, origin_list);
596 }
W. James MacLean254a08762020-08-12 14:09:37597};
598
599// This test verifies that opt-in isolation takes precedence over command-line
600// isolation. It loads an opt-in isolated base origin (which would have
601// otherwise been isolated via command-line isolation), and then loads a child
602// frame sub-origin which should-not be isolated (but would have been if the
603// base origin was command-line isolated).
Domenic Denicola7dbd3d12021-01-08 21:26:56604IN_PROC_BROWSER_TEST_F(OriginIsolationOptInHeaderCommandLineTest,
W. James MacLean254a08762020-08-12 14:09:37605 OptInOverridesCommandLine) {
Domenic Denicola7dbd3d12021-01-08 21:26:56606 SetHeaderValue("?1");
W. James MacLean254a08762020-08-12 14:09:37607 // Start off with an isolated base-origin in an a(a) configuration, then
608 // navigate the subframe to a sub-origin not requesting isolation.
609 // Note: this works because we serve mock headers with the base origin's html
Domenic Denicola7dbd3d12021-01-08 21:26:56610 // file, which set the header.
W. James MacLean254a08762020-08-12 14:09:37611 GURL isolated_base_origin_url(https_server()->GetURL(
612 "foo.com", "/isolated_base_origin_with_subframe.html"));
613 GURL non_isolated_sub_origin(
614 https_server()->GetURL("non_isolated.foo.com", "/title1.html"));
615 EXPECT_TRUE(NavigateToURL(shell(), isolated_base_origin_url));
W. James MacLean222a2472020-08-14 22:00:22616 // The .html main frame has two iframes, this test only uses the first one.
Dave Tapuska4eea4112021-09-16 15:49:21617 EXPECT_EQ(3u, CollectAllRenderFrameHosts(shell()->web_contents()).size());
W. James MacLean254a08762020-08-12 14:09:37618
Carlos Caballero15caeeb2021-10-27 09:57:55619 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
W. James MacLean254a08762020-08-12 14:09:37620 FrameTreeNode* child_frame_node = root->child_at(0);
W. James MacLean222a2472020-08-14 22:00:22621
Lukasz Anforowicz69c25dfd2020-11-12 21:50:20622 EXPECT_TRUE(
623 NavigateToURLFromRenderer(child_frame_node, non_isolated_sub_origin));
W. James MacLean254a08762020-08-12 14:09:37624
625 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
W. James MacLean7f76c2202021-11-15 16:27:49626 EXPECT_TRUE(policy
W. James MacLeand42fa812021-11-18 22:59:26627 ->DetermineOriginAgentClusterIsolation(
W. James MacLean7f76c2202021-11-15 16:27:49628 root->current_frame_host()
629 ->GetSiteInstance()
630 ->GetIsolationContext(),
631 url::Origin::Create(isolated_base_origin_url),
632 MakeOACIsolationState(false))
633 .requires_origin_keyed_process());
634 EXPECT_FALSE(policy
W. James MacLeand42fa812021-11-18 22:59:26635 ->DetermineOriginAgentClusterIsolation(
W. James MacLean7f76c2202021-11-15 16:27:49636 root->current_frame_host()
637 ->GetSiteInstance()
638 ->GetIsolationContext(),
639 url::Origin::Create(non_isolated_sub_origin),
640 MakeOACIsolationState(false))
641 .requires_origin_keyed_process());
W. James MacLean254a08762020-08-12 14:09:37642 // Make sure the child (i.e. sub-origin) is not isolated.
643 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
644 child_frame_node->current_frame_host()->GetSiteInstance());
645 EXPECT_EQ(
646 GURL("https://foo.com"),
647 child_frame_node->current_frame_host()->GetSiteInstance()->GetSiteURL());
648 // The following test passes because IsIsolatedOrigin doesn't distinguish
649 // between command-line isolation and opt-in isolation.
650 EXPECT_TRUE(policy->IsIsolatedOrigin(
651 root->current_frame_host()->GetSiteInstance()->GetIsolationContext(),
W. James MacLean46cf26212020-10-01 16:43:37652 url::Origin::Create(non_isolated_sub_origin),
653 false /* origin_requests_isolation */));
W. James MacLean254a08762020-08-12 14:09:37654
W. James MacLean222a2472020-08-14 22:00:22655 // Make sure the opt-in isolated origin is origin-keyed, and the non-opt-in
656 // origin is site-keyed.
657 EXPECT_TRUE(root->current_frame_host()
658 ->GetSiteInstance()
659 ->GetSiteInfo()
W. James MacLean7f76c2202021-11-15 16:27:49660 .requires_origin_keyed_process());
W. James MacLean222a2472020-08-14 22:00:22661 EXPECT_FALSE(child_frame_node->current_frame_host()
662 ->GetSiteInstance()
663 ->GetSiteInfo()
W. James MacLean7f76c2202021-11-15 16:27:49664 .requires_origin_keyed_process());
W. James MacLean222a2472020-08-14 22:00:22665
W. James MacLean254a08762020-08-12 14:09:37666 // Make sure the master opt-in list has the base origin isolated and the sub
667 // origin not isolated.
W. James MacLeand7eb1562020-11-17 17:56:10668 BrowserContext* browser_context = web_contents()->GetBrowserContext();
W. James MacLeanc07dc41b2022-07-25 18:52:16669 EXPECT_TRUE(policy->HasOriginEverRequestedOriginAgentClusterValue(
W. James MacLeand7eb1562020-11-17 17:56:10670 browser_context, url::Origin::Create(isolated_base_origin_url)));
W. James MacLeanc07dc41b2022-07-25 18:52:16671 EXPECT_FALSE(policy->HasOriginEverRequestedOriginAgentClusterValue(
W. James MacLeand7eb1562020-11-17 17:56:10672 browser_context, url::Origin::Create(non_isolated_sub_origin)));
W. James MacLean254a08762020-08-12 14:09:37673}
674
W. James MacLeand8d31f0f2023-05-26 19:15:57675// A test to verify that an origin with a trailing dot in the domain name
676// doesn't crash when it opts-out of origin isolation when
677// kOriginAgentClusterDefaultEnabled is enabled.
678IN_PROC_BROWSER_TEST_F(OriginIsolationOptInHeaderTest,
679 TrailingDotDomainOptOutDoesNotCrash) {
680 GURL dotted_nonisolated_url(
681 https_server()->GetURL("a.com.", "/isolate_origin"));
682
683 // Set header to opt this domain out of default OriginAgentCluster.
684 SetHeaderValue("?0");
685 EXPECT_TRUE(NavigateToURL(shell(), dotted_nonisolated_url));
686 url::Origin origin(url::Origin::Create(dotted_nonisolated_url));
687 EXPECT_FALSE(ShouldOriginGetOptInProcessIsolation(origin));
688}
689
690// A test to confirm that "a.com." is treated as a separate host (and hence
691// a separate origin) from "a.com". See example at
692// https://url.spec.whatwg.org/#concept-domain.
693IN_PROC_BROWSER_TEST_F(OriginIsolationOptInHeaderTest,
694 TrailingDotDomainIsolatesSeparately1) {
695 GURL main_frame_url(https_server()->GetURL(
696 "foo.com", "/cross_site_iframe_factory.html?foo.com(foo.com,foo.com)"));
697 GURL isolated_url(https_server()->GetURL("a.com", "/isolate_origin"));
698 GURL dotted_isolated_url(https_server()->GetURL("a.com.", "/isolate_origin"));
699 SetHeaderValue("?1");
700
701 // Create page with sibling iframes.
702 EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
703 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
704 EXPECT_EQ(2u, root->child_count());
705 FrameTreeNode* child0_frame_node = root->child_at(0);
706 FrameTreeNode* child1_frame_node = root->child_at(1);
707 EXPECT_TRUE(NavigateToURLFromRenderer(child0_frame_node, isolated_url));
708 EXPECT_TRUE(
709 NavigateToURLFromRenderer(child1_frame_node, dotted_isolated_url));
710
711 url::Origin child0_origin(url::Origin::Create(isolated_url));
712 url::Origin child1_origin(url::Origin::Create(dotted_isolated_url));
713 EXPECT_NE(isolated_url, dotted_isolated_url);
714 EXPECT_NE(child0_origin, child1_origin);
715
716 EXPECT_TRUE(ShouldOriginGetOptInProcessIsolation(child0_origin));
717 EXPECT_TRUE(ShouldOriginGetOptInProcessIsolation(child1_origin));
718
719 scoped_refptr<SiteInstanceImpl> child0_site_instance =
720 child0_frame_node->current_frame_host()->GetSiteInstance();
721 scoped_refptr<SiteInstanceImpl> child1_site_instance =
722 child1_frame_node->current_frame_host()->GetSiteInstance();
723 EXPECT_NE(child0_site_instance, child1_site_instance);
724 EXPECT_NE(child0_site_instance->GetProcess(),
725 child1_site_instance->GetProcess());
726}
727
728// A test similar to TrailingDotDomainIsolatesSeparately1, but this time the
729// "a.com" domain does not opt-in via a header, and does not get an origin-
730// keyed process. Thus, it ends up in a separate process from "a.com.".
731IN_PROC_BROWSER_TEST_F(OriginIsolationOptInHeaderTest,
732 TrailingDotDomainIsolatesSeparately2) {
733 GURL main_frame_url(https_server()->GetURL(
734 "foo.com", "/cross_site_iframe_factory.html?foo.com(foo.com,foo.com)"));
735 GURL non_isolated_url(https_server()->GetURL("a.com", "/title1.html"));
736 GURL dotted_isolated_url(https_server()->GetURL("a.com.", "/isolate_origin"));
737
738 // Create page with sibling iframes.
739 EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
740 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
741 EXPECT_EQ(2u, root->child_count());
742 FrameTreeNode* child0_frame_node = root->child_at(0);
743 FrameTreeNode* child1_frame_node = root->child_at(1);
744 SetHeaderValue("?1");
745 EXPECT_TRUE(
746 NavigateToURLFromRenderer(child0_frame_node, dotted_isolated_url));
747 SetHeaderValue("");
748 EXPECT_TRUE(NavigateToURLFromRenderer(child1_frame_node, non_isolated_url));
749
750 url::Origin child0_origin(url::Origin::Create(dotted_isolated_url));
751 url::Origin child1_origin(url::Origin::Create(non_isolated_url));
752
753 EXPECT_TRUE(ShouldOriginGetOptInProcessIsolation(child0_origin));
754 EXPECT_FALSE(ShouldOriginGetOptInProcessIsolation(child1_origin));
755
756 scoped_refptr<SiteInstanceImpl> child0_site_instance =
757 child0_frame_node->current_frame_host()->GetSiteInstance();
758 scoped_refptr<SiteInstanceImpl> child1_site_instance =
759 child1_frame_node->current_frame_host()->GetSiteInstance();
760 EXPECT_NE(child0_site_instance, child1_site_instance);
761 EXPECT_NE(child0_site_instance->GetProcess(),
762 child1_site_instance->GetProcess());
763}
764
W. James MacLeanbae2bd382022-06-13 14:53:40765// A test to confirm that if an Origin-Agent-Cluster header is encountered (but
766// not committed) as part of a redirect, that it does not opt-in to
767// OriginAgentCluster isolation. The setup in this test is subtle, since in
768// order for the call to NavigationRequest::OnRequestRedirected() to attempt to
769// create a new SiteInstance, we must load the same origin the redirect wants to
770// use, and load it without OriginAgentCluster isolation. Prior to the fix for
771// https://crbug.com/1329061 the redirect would result in opting the origin into
772// OriginAgentCluster isolation since no global walk is present to detect that
773// it has already been loaded without.
774IN_PROC_BROWSER_TEST_F(OriginIsolationOptInHeaderTest,
775 RedirectSameSiteWithOACDoesntOptIn) {
776 GURL main_frame_url(https_server()->GetURL(
777 "foo.com", "/cross_site_iframe_factory.html?foo.com(foo.com)"));
778 GURL redirect_url(https_server()->GetURL("foo.com", "/redirect_me"));
779 GURL expected_commit_url(https_server()->GetURL("foo.com", "/title1.html"));
780 url::Origin origin(url::Origin::Create(main_frame_url));
781
782 EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
783 EXPECT_FALSE(ShouldOriginGetOptInProcessIsolation(origin));
784 EXPECT_EQ(2u, CollectAllRenderFrameHosts(shell()->web_contents()).size());
785 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
786 FrameTreeNode* child_frame_node = root->child_at(0);
787
788 SetRedirectTarget("/title1.html");
789 SetHeaderValue("?1");
790 EXPECT_TRUE(NavigateToURLFromRenderer(child_frame_node, redirect_url,
791 expected_commit_url));
792 // This next line verifies that the OriginAgentCluster header sent with the
793 // 301 redirect failed to opt foo.com into OriginAgentCluster isolation, as
794 // it should. The check will fail if the origin was opted-in to
795 // OriginAgentCluster isolation.
796 EXPECT_FALSE(ShouldOriginGetOptInProcessIsolation(origin));
797}
798
799// Same as the preceding test, but the redirect is cross-site.
800IN_PROC_BROWSER_TEST_F(OriginIsolationOptInHeaderTest,
801 RedirectCrossSiteWithOACDoesntOptIn) {
802 GURL main_frame_url(https_server()->GetURL(
803 "foo.com", "/cross_site_iframe_factory.html?foo.com(foo.com)"));
804 GURL redirect_url(https_server()->GetURL("bar.com", "/redirect_me"));
805 GURL expected_commit_url(https_server()->GetURL("foo.com", "/title1.html"));
806 url::Origin origin(url::Origin::Create(main_frame_url));
807
808 EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
809 EXPECT_FALSE(ShouldOriginGetOptInProcessIsolation(origin));
810
811 EXPECT_EQ(2u, CollectAllRenderFrameHosts(shell()->web_contents()).size());
812 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
813 FrameTreeNode* child_frame_node = root->child_at(0);
814
815 SetRedirectTarget(expected_commit_url.spec());
816 SetHeaderValue("?1");
817 EXPECT_TRUE(NavigateToURLFromRenderer(child_frame_node, redirect_url,
818 expected_commit_url));
819
820 // This next line verifies that the OriginAgentCluster header sent with the
821 // 301 redirect failed to opt foo.com into OriginAgentCluster isolation, as
822 // it should. The check will fail if the origin was opted-in to
823 // OriginAgentCluster isolation.
824 EXPECT_FALSE(ShouldOriginGetOptInProcessIsolation(origin));
825}
826
Domenic Denicola14103152020-04-03 19:40:45827// This tests that header-based opt-in causes the origin to end up in the
828// isolated origins list.
829IN_PROC_BROWSER_TEST_F(OriginIsolationOptInHeaderTest, Basic) {
W. James MacLeana485f0e2021-01-29 17:18:05830 base::HistogramTester histograms;
Domenic Denicola14103152020-04-03 19:40:45831 SetHeaderValue("?1");
832
Marijn Kruisselbrink1fee8062020-05-08 00:54:44833 GURL url(https_server()->GetURL("isolated.foo.com", "/isolate_origin"));
Domenic Denicola14103152020-04-03 19:40:45834 url::Origin origin(url::Origin::Create(url));
835
W. James MacLean7f76c2202021-11-15 16:27:49836 EXPECT_FALSE(ShouldOriginGetOptInProcessIsolation(origin));
Domenic Denicola14103152020-04-03 19:40:45837 EXPECT_TRUE(NavigateToURL(shell(), url));
W. James MacLean7f76c2202021-11-15 16:27:49838 EXPECT_TRUE(ShouldOriginGetOptInProcessIsolation(origin));
W. James MacLeana485f0e2021-01-29 17:18:05839
840 EXPECT_THAT(
841 histograms.GetAllSamples("Navigation.OriginAgentCluster.Result"),
842 testing::ElementsAre(base::Bucket(
843 static_cast<int>(NavigationRequest::OriginAgentClusterEndResult::
844 kRequestedAndOriginKeyed),
845 1)));
Domenic Denicola14103152020-04-03 19:40:45846}
847
W. James MacLeand8d31f0f2023-05-26 19:15:57848// A test to ensure that origins whose host has a trailing dot pass the
849// validation checks for explicit opt-ins and opt-outs. This is an
850// `OriginKeyedProcessByDefaultTest` test in order that the explicit opt-out
851// will be tracked. Note: failure for either part of this test will involve
852// crashing on a CHECK in
853// ChildProcessSecurityPolicyImpl::AddOriginIsolationStateForBrowsingInstance():
854IN_PROC_BROWSER_TEST_F(OriginKeyedProcessByDefaultTest,
855 HostWithTrailingDotAllowed) {
856 // Explicit opt-in with a trailing dot.
857 SetHeaderValue("?1");
858 GURL opt_in_url(https_server()->GetURL("opt-in.foo.com.", "/isolate_origin"));
859 url::Origin opt_in_origin(url::Origin::Create(opt_in_url));
860
861 EXPECT_FALSE(ShouldOriginGetOptInProcessIsolation(opt_in_origin));
862 EXPECT_TRUE(NavigateToURL(shell(), opt_in_url));
863 EXPECT_TRUE(ShouldOriginGetOptInProcessIsolation(opt_in_origin));
864
865 // Explicit opt-out with a trailing dot.
866 SetHeaderValue("?0");
867 GURL opt_out_url(
868 https_server()->GetURL("opt-out.foo.com.", "/isolate_origin"));
869 url::Origin opt_out_origin(url::Origin::Create(opt_out_url));
870
871 EXPECT_FALSE(ShouldOriginGetOptInProcessIsolation(opt_out_origin));
872 EXPECT_TRUE(NavigateToURL(shell(), opt_out_url));
873 EXPECT_FALSE(ShouldOriginGetOptInProcessIsolation(opt_out_origin));
874}
875
W. James MacLean53e24b72023-05-09 20:57:07876// A simple test that, when OAC-by-default is enabled with process-isolation, an
877// origin that receives default OAC is put in an origin-keyed process.
878IN_PROC_BROWSER_TEST_F(OriginKeyedProcessByDefaultTest,
879 DefaultIsOriginKeyedProcess) {
880 GURL test_url(https_server()->GetURL("foo.com", "/title1.html"));
881 EXPECT_TRUE(NavigateToURL(shell(), test_url));
882 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
883 scoped_refptr<SiteInstanceImpl> site_instance =
884 root->current_frame_host()->GetSiteInstance();
885 OriginAgentClusterIsolationState isolation_state =
886 DetermineOriginAgentClusterIsolation(site_instance.get(), test_url);
887 // Even though this request has no OriginAgentCluster header, it should get
888 // an origin-keyed process by default.
889 EXPECT_TRUE(isolation_state.is_origin_agent_cluster());
890 EXPECT_TRUE(isolation_state.requires_origin_keyed_process());
891 EXPECT_TRUE(site_instance->GetSiteInfo().requires_origin_keyed_process());
Camille Lamy5ce9b962025-08-08 12:10:45892 EXPECT_EQ(AgentClusterKey::OACStatus::kOriginKeyedByDefault,
893 site_instance->GetSiteInfo().oac_status());
W. James MacLean53e24b72023-05-09 20:57:07894}
895
W. James MacLeanb68b42dd2024-06-05 20:35:43896// A test to make sure that a renderer-initiated navigation from a default-
897// isolated frame to about:blank doesn't crash on a ProcessLock mismatch.
898IN_PROC_BROWSER_TEST_F(OriginKeyedProcessByDefaultTest,
899 RendererInitiatedNavigationToAboutBlankSucceeds) {
900 GURL test_url(https_server()->GetURL("foo.com", "/title1.html"));
901 EXPECT_TRUE(NavigateToURL(shell(), test_url));
902
903 // Verify the main frame got an origin-keyed process by default.
904 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
905 scoped_refptr<SiteInstanceImpl> site_instance =
906 root->current_frame_host()->GetSiteInstance();
907 EXPECT_TRUE(site_instance->GetSiteInfo().requires_origin_keyed_process());
Camille Lamy5ce9b962025-08-08 12:10:45908 EXPECT_EQ(AgentClusterKey::OACStatus::kOriginKeyedByDefault,
909 site_instance->GetSiteInfo().oac_status());
W. James MacLeanb68b42dd2024-06-05 20:35:43910
911 // Record the origin of the isolated frame.
912 std::string initial_origin = EvalJs(shell(), "origin").ExtractString();
913 EXPECT_EQ(url::Origin::Create(test_url).GetURL(), GURL(initial_origin));
914
915 // Renderer-initiated navigation to about:blank.
916 TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
917 EXPECT_TRUE(ExecJs(shell(), "location.href = 'about:blank';"));
918 navigation_observer.Wait();
919
920 // Expect that the about:blank frame inherits the origin of the initiator.
921 // Also, this gives us additional verification that the navigation succeeded
922 // without hitting the ProcessLock check.
923 EXPECT_EQ(initial_origin, EvalJs(shell(), "origin").ExtractString());
924 scoped_refptr<SiteInstanceImpl> new_site_instance =
925 root->current_frame_host()->GetSiteInstance();
926 // Note: the site_instance has changed, due to the proactive BrowsingInstance
927 // swap done to make the previous page eligible for back-forward cache.
928 // Note: some bots may run this test with BFCache disabled, so we need to
929 // handle both cases here.
930 if (base::FeatureList::IsEnabled(features::kBackForwardCache)) {
931 EXPECT_NE(site_instance, new_site_instance);
932 } else {
933 EXPECT_EQ(site_instance, new_site_instance);
934 }
935 EXPECT_EQ(site_instance->GetSiteInfo(), site_instance->GetSiteInfo());
936 EXPECT_TRUE(new_site_instance->GetSiteInfo().requires_origin_keyed_process());
937}
938
939// A test to make sure that a renderer-initiated navigation from a default-
940// isolated frame to about:blank doesn't crash on a ProcessLock mismatch.
941// This test is similar to RendererInitiatedNavigationToAboutBlankSucceeds
942// but with BFCache disabled.
943IN_PROC_BROWSER_TEST_F(
944 OriginKeyedProcessByDefaultTest,
945 RendererInitiatedNavigationToAboutBlankSucceeds_BFCacheDisabled) {
946 DisableBackForwardCacheForTesting(
947 web_contents(), BackForwardCacheImpl::TEST_REQUIRES_NO_CACHING);
948 GURL test_url(https_server()->GetURL("foo.com", "/title1.html"));
949 EXPECT_TRUE(NavigateToURL(shell(), test_url));
950
951 // Verify the main frame got an origin-keyed process by default.
952 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
953 scoped_refptr<SiteInstanceImpl> site_instance =
954 root->current_frame_host()->GetSiteInstance();
955 EXPECT_TRUE(site_instance->GetSiteInfo().requires_origin_keyed_process());
Camille Lamy5ce9b962025-08-08 12:10:45956 EXPECT_EQ(AgentClusterKey::OACStatus::kOriginKeyedByDefault,
957 site_instance->GetSiteInfo().oac_status());
W. James MacLeanb68b42dd2024-06-05 20:35:43958
959 // Record the origin of the isolated frame.
960 std::string initial_origin = EvalJs(shell(), "origin").ExtractString();
961 EXPECT_EQ(url::Origin::Create(test_url).GetURL(), GURL(initial_origin));
962
963 // Renderer-initiated navigation to about:blank.
964 TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
965 EXPECT_TRUE(ExecJs(shell(), "location.href = 'about:blank';"));
966 navigation_observer.Wait();
967
968 // Expect that the about:blank frame inherits the origin of the initiator.
969 // Also, this gives us additional verification that the navigation succeeded
970 // without hitting the ProcessLock check.
971 EXPECT_EQ(initial_origin, EvalJs(shell(), "origin").ExtractString());
972 scoped_refptr<SiteInstanceImpl> new_site_instance =
973 root->current_frame_host()->GetSiteInstance();
974 // Note: with BFCache disabled, the site_instance does not change.
975 EXPECT_EQ(site_instance, new_site_instance);
976 EXPECT_EQ(site_instance->GetSiteInfo(), site_instance->GetSiteInfo());
977 EXPECT_TRUE(new_site_instance->GetSiteInfo().requires_origin_keyed_process());
978}
979
W. James MacLean53e24b72023-05-09 20:57:07980// The same as for DefaultOptInIsIsolated, but testing on a subframe.
981IN_PROC_BROWSER_TEST_F(OriginKeyedProcessByDefaultTest,
982 SubframeDefaultIsOriginKeyedProcess) {
983 GURL test_url(https_server()->GetURL("foo.com",
984 "/cross_site_iframe_factory.html?"
985 "foo.com(foo.com)"));
986 EXPECT_TRUE(NavigateToURL(shell(), test_url));
987 EXPECT_EQ(2u, CollectAllRenderFrameHosts(shell()->web_contents()).size());
988 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
989 FrameTreeNode* child_frame_node = root->child_at(0);
990
991 // Even though this request has no OriginAgentCluster header, it should get
992 // an origin-keyed process by default.
993 SetHeaderValue("");
994 GURL default_isolated_url(
995 https_server()->GetURL("isolated.foo.com", "/title1.html"));
996 EXPECT_TRUE(
997 NavigateToURLFromRenderer(child_frame_node, default_isolated_url));
998
999 scoped_refptr<SiteInstanceImpl> root_site_instance =
1000 root->current_frame_host()->GetSiteInstance();
1001 scoped_refptr<SiteInstanceImpl> child_site_instance =
1002 child_frame_node->current_frame_host()->GetSiteInstance();
1003 OriginAgentClusterIsolationState child_isolation_state =
1004 DetermineOriginAgentClusterIsolation(child_site_instance.get(),
1005 default_isolated_url);
1006 EXPECT_NE(child_site_instance, root_site_instance);
1007 EXPECT_NE(child_site_instance->GetProcess(),
1008 root_site_instance->GetProcess());
1009 EXPECT_TRUE(child_isolation_state.is_origin_agent_cluster());
1010 EXPECT_TRUE(child_isolation_state.requires_origin_keyed_process());
W. James MacLeanae8486cb2023-05-24 15:57:131011 EXPECT_EQ(
1012 root_site_instance->GetIsolationContext().default_isolation_state(),
1013 child_site_instance->GetIsolationContext().default_isolation_state());
W. James MacLean53e24b72023-05-09 20:57:071014 EXPECT_TRUE(
1015 child_site_instance->GetSiteInfo().requires_origin_keyed_process());
Camille Lamy5ce9b962025-08-08 12:10:451016 EXPECT_EQ(AgentClusterKey::OACStatus::kOriginKeyedByDefault,
1017 child_site_instance->GetSiteInfo().oac_status());
W. James MacLean2a84fbf2023-05-12 18:13:431018
1019 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
1020 IsolationContext isolation_context =
1021 root_site_instance->GetIsolationContext();
1022 // Verify that we're not explicitly tracking the origin for
1023 // `default_isolated_url`.
1024 EXPECT_EQ(static_cast<OriginAgentClusterIsolationState*>(nullptr),
1025 policy->LookupOriginIsolationStateForTesting(
1026 isolation_context.browsing_instance_id(),
1027 url::Origin::Create(default_isolated_url)));
1028
1029 // Now trigger a global walk by attempting to create a non-isolated version of
1030 // the same origin.
1031 GURL attempted_non_isolated_url(
1032 https_server()->GetURL("isolated.foo.com", "/isolate_origin"));
1033 SetHeaderValue("?0");
1034 EXPECT_TRUE(
1035 NavigateToURLFromRenderer(child_frame_node, attempted_non_isolated_url));
1036 // Now the origin should be explicitly tracked, even though it continues to
1037 // have the default isolation state as defined for the current
1038 // BrowsingInstance.
1039 OriginAgentClusterIsolationState* isolation_state2 =
1040 policy->LookupOriginIsolationStateForTesting(
1041 isolation_context.browsing_instance_id(),
1042 url::Origin::Create(default_isolated_url));
1043 ASSERT_NE(static_cast<OriginAgentClusterIsolationState*>(nullptr),
1044 isolation_state2);
1045 EXPECT_TRUE(isolation_state2->is_origin_agent_cluster());
1046 EXPECT_TRUE(isolation_state2->requires_origin_keyed_process());
1047}
1048
Jiacheng Guo782c9ac2024-08-01 08:14:241049// The test ExplicitOptInRespected tests the speculative RFH created before
1050// receiving the OAC headers. The delay must be set to zero so that the
1051// speculative RFH is always created before receiving the header.
1052class OriginKeyedProcessByDefaultTestWithoutSpeculativeRFHDelay
1053 : public OriginKeyedProcessByDefaultTest {
1054 public:
1055 OriginKeyedProcessByDefaultTestWithoutSpeculativeRFHDelay() {
1056 feature_list_for_defer_speculative_rfh_.InitAndEnableFeatureWithParameters(
1057 features::kDeferSpeculativeRFHCreation,
1058 {{"create_speculative_rfh_delay_ms", "0"}});
1059 }
1060
1061 private:
1062 base::test::ScopedFeatureList feature_list_for_defer_speculative_rfh_;
1063};
1064
W. James MacLean2a84fbf2023-05-12 18:13:431065// Using origin-keyed processes by default faces a challenge for speculative
1066// RenderFrameHosts, which are created before any OAC headers arrive.
1067// Note: the origin is tracked even though the SiteInfo still says it is an
1068// origin-keyed process by default.
Jiacheng Guo782c9ac2024-08-01 08:14:241069IN_PROC_BROWSER_TEST_F(
1070 OriginKeyedProcessByDefaultTestWithoutSpeculativeRFHDelay,
1071 ExplicitOptInRespected) {
W. James MacLean2a84fbf2023-05-12 18:13:431072 GURL test_url(https_server()->GetURL("foo.com",
1073 "/cross_site_iframe_factory.html?"
1074 "foo.com(foo.com)"));
1075 EXPECT_TRUE(NavigateToURL(shell(), test_url));
1076 EXPECT_EQ(2u, CollectAllRenderFrameHosts(shell()->web_contents()).size());
1077 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
1078 FrameTreeNode* child_frame_node = root->child_at(0);
1079
1080 // This request explicitly opts-in to OAC, and should get process isolation.
1081 // Note the use of the "isolate_origin" relative path below to force
1082 // processing of the (non-empty) OAC header.
1083 SetHeaderValue("?1");
1084 GURL explicit_isolated_url(
1085 https_server()->GetURL("isolated.foo.com", "/isolate_origin"));
1086 EXPECT_TRUE(
1087 NavigateToURLFromRenderer(child_frame_node, explicit_isolated_url));
1088
1089 auto* site_instance =
1090 child_frame_node->current_frame_host()->GetSiteInstance();
1091 OriginAgentClusterIsolationState isolation_state =
1092 DetermineOriginAgentClusterIsolation(site_instance,
1093 explicit_isolated_url);
1094 EXPECT_TRUE(isolation_state.is_origin_agent_cluster());
1095 EXPECT_TRUE(isolation_state.requires_origin_keyed_process());
1096 EXPECT_TRUE(site_instance->GetSiteInfo().requires_origin_keyed_process());
1097 // In this scenario, the explicit opt-in ends up using a SiteInstance that was
1098 // created for the speculative RFH, and with requires_origin_keyed_process on
1099 // by default. Since we don't want to alter the underlying SiteInfo after
1100 // it's been used to create a ProcessLock, we leave this case as "by_default"
1101 // in the SiteInfo since the isolation behavior is the same.
1102 //
1103 // Note that if the speculative RFH had been created after a previous instance
1104 // of the origin had been explicitly opted-in, then
1105 // `requires_origin_keyed_process_by_default()` would return false in that
1106 // case. This can happen in a cross-origin redirect from A to B, where B has
1107 // an opt-in header. We would create a speculative RFH for A, throw it away
1108 // when the redirect happens, and wait to create the RFH for B until headers
1109 // have arrived.
Camille Lamy5ce9b962025-08-08 12:10:451110 EXPECT_EQ(AgentClusterKey::OACStatus::kOriginKeyedByDefault,
1111 site_instance->GetSiteInfo().oac_status());
W. James MacLean2a84fbf2023-05-12 18:13:431112
1113 // Verify the explicit opt-in is being tracked.
1114 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
1115 IsolationContext isolation_context =
1116 root->current_frame_host()->GetSiteInstance()->GetIsolationContext();
1117 OriginAgentClusterIsolationState* isolation_state2 =
1118 policy->LookupOriginIsolationStateForTesting(
1119 isolation_context.browsing_instance_id(),
1120 url::Origin::Create(explicit_isolated_url));
1121 ASSERT_NE(static_cast<OriginAgentClusterIsolationState*>(nullptr),
1122 isolation_state2);
1123 EXPECT_TRUE(isolation_state2->is_origin_agent_cluster());
1124 EXPECT_TRUE(isolation_state2->requires_origin_keyed_process());
W. James MacLean53e24b72023-05-09 20:57:071125}
1126
1127// Process-isolated OAC-by-default should not process isolate a navigation that
1128// explicitly opts-out. This test is important, in part, for making sure all the
1129// CanAccessDataForOrigin checks encountered during the navigation pass.
1130IN_PROC_BROWSER_TEST_F(OriginKeyedProcessByDefaultTest,
1131 ExplicitOptOutRespected) {
1132 GURL test_url(https_server()->GetURL("foo.com",
1133 "/cross_site_iframe_factory.html?"
1134 "foo.com(foo.com)"));
1135 EXPECT_TRUE(NavigateToURL(shell(), test_url));
1136 EXPECT_EQ(2u, CollectAllRenderFrameHosts(shell()->web_contents()).size());
1137 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
1138 FrameTreeNode* child_frame_node = root->child_at(0);
1139
1140 // This request explicitly opts-out of OAC, and should not get process
1141 // isolation. Note the use of the "isolate_origin" relative path below to
1142 // force processing of the (non-empty) OAC header.
1143 SetHeaderValue("?0");
1144 GURL default_not_isolated_url(
1145 https_server()->GetURL("not-isolated.foo.com", "/isolate_origin"));
1146 EXPECT_TRUE(
1147 NavigateToURLFromRenderer(child_frame_node, default_not_isolated_url));
1148
W. James MacLeanae8486cb2023-05-24 15:57:131149 auto* child_site_instance =
W. James MacLean53e24b72023-05-09 20:57:071150 child_frame_node->current_frame_host()->GetSiteInstance();
W. James MacLeanae8486cb2023-05-24 15:57:131151 // The child should be in a separate process from the main frame despite
1152 // opting-out, because the child is now in a site-keyed process while the
1153 // main frame is in an origin-keyed process (as verified below).
1154 EXPECT_NE(child_site_instance->GetProcess(),
1155 root->current_frame_host()->GetSiteInstance()->GetProcess());
1156
W. James MacLean53e24b72023-05-09 20:57:071157 OriginAgentClusterIsolationState isolation_state =
W. James MacLeanae8486cb2023-05-24 15:57:131158 DetermineOriginAgentClusterIsolation(child_site_instance,
W. James MacLean53e24b72023-05-09 20:57:071159 default_not_isolated_url);
W. James MacLean2a84fbf2023-05-12 18:13:431160 EXPECT_FALSE(isolation_state.is_origin_agent_cluster());
1161 EXPECT_FALSE(isolation_state.requires_origin_keyed_process());
W. James MacLean2a84fbf2023-05-12 18:13:431162 EXPECT_FALSE(
W. James MacLeanae8486cb2023-05-24 15:57:131163 child_site_instance->GetSiteInfo().requires_origin_keyed_process());
Camille Lamy5ce9b962025-08-08 12:10:451164 EXPECT_EQ(AgentClusterKey::OACStatus::kSiteKeyedByHeader,
1165 child_site_instance->GetSiteInfo().oac_status());
W. James MacLean2a84fbf2023-05-12 18:13:431166
1167 // Verify the explicit opt-out is being tracked.
1168 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
1169 IsolationContext isolation_context =
1170 root->current_frame_host()->GetSiteInstance()->GetIsolationContext();
1171 OriginAgentClusterIsolationState* isolation_state2 =
1172 policy->LookupOriginIsolationStateForTesting(
1173 isolation_context.browsing_instance_id(),
1174 url::Origin::Create(default_not_isolated_url));
1175 ASSERT_NE(static_cast<OriginAgentClusterIsolationState*>(nullptr),
1176 isolation_state2);
1177 EXPECT_FALSE(isolation_state2->is_origin_agent_cluster());
1178 EXPECT_FALSE(isolation_state2->requires_origin_keyed_process());
W. James MacLean53e24b72023-05-09 20:57:071179}
1180
W. James MacLeanae8486cb2023-05-24 15:57:131181namespace {
1182
1183void TestDefaultIsolationForFrame(
1184 FrameTreeNode* ftn,
1185 const GURL& default_isolated_url,
1186 bool expect_origin_agent_cluster,
1187 bool expect_requires_origin_keyed_process,
1188 bool expect_default_requires_origin_keyed_process) {
1189 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
1190 auto* site_instance = ftn->current_frame_host()->GetSiteInstance();
1191 OriginAgentClusterIsolationState isolation_state =
1192 IsolatedOriginTestBase::DetermineOriginAgentClusterIsolation(
1193 site_instance, default_isolated_url);
1194 const SiteInfo& site_info = site_instance->GetSiteInfo();
1195 EXPECT_EQ(expect_origin_agent_cluster,
1196 isolation_state.is_origin_agent_cluster());
1197 EXPECT_EQ(expect_requires_origin_keyed_process,
1198 isolation_state.requires_origin_keyed_process());
1199 EXPECT_EQ(expect_requires_origin_keyed_process,
1200 site_info.requires_origin_keyed_process());
1201 EXPECT_EQ(expect_default_requires_origin_keyed_process,
Camille Lamy5ce9b962025-08-08 12:10:451202 site_info.oac_status() ==
1203 AgentClusterKey::OACStatus::kOriginKeyedByDefault);
W. James MacLeanae8486cb2023-05-24 15:57:131204
1205 // Verify that we're not explicitly tracking the origin we isolated by
1206 // default.
1207 IsolationContext isolation_context = site_instance->GetIsolationContext();
1208 EXPECT_EQ(static_cast<OriginAgentClusterIsolationState*>(nullptr),
1209 policy->LookupOriginIsolationStateForTesting(
1210 isolation_context.browsing_instance_id(),
1211 url::Origin::Create(default_isolated_url)));
1212}
1213
1214} // namespace
1215
1216// This test verifies that locking the definition of default isolation to
1217// individual BrowsingInstances works correctly when the underlying feature
1218// is changed dynamically.
1219IN_PROC_BROWSER_TEST_F(OriginKeyedProcessByDefaultTest,
1220 DynamicEnterprisePolicyChange) {
1221 GURL test_url(https_server()->GetURL("foo.com",
1222 "/cross_site_iframe_factory.html?"
1223 "foo.com(foo.com)"));
1224 SetHeaderValue("");
1225 GURL default_isolated_url(
1226 https_server()->GetURL("isolated.foo.com", "/title1.html"));
1227
1228 // Setup first BrowsingInstance. This one will have default isolation with
1229 // origin_agent_cluster and requests_origin_keyed_process (by default) true.
1230 Shell* shell1 = shell();
1231 auto* web_contents1 = static_cast<WebContentsImpl*>(shell1->web_contents());
1232
1233 EXPECT_TRUE(NavigateToURL(shell1, test_url));
1234 EXPECT_EQ(2u, CollectAllRenderFrameHosts(web_contents1).size());
1235 FrameTreeNode* root1 = web_contents1->GetPrimaryFrameTree().root();
1236 FrameTreeNode* child_frame_node1 = root1->child_at(0);
1237
1238 // Load a frame into the first BrowsingInstance.
1239 // Even though this request has no OriginAgentCluster header, it should get
1240 // process-isolated OAC by default.
1241 EXPECT_TRUE(
1242 NavigateToURLFromRenderer(child_frame_node1, default_isolated_url));
1243 EXPECT_NE(
1244 child_frame_node1->current_frame_host()->GetSiteInstance()->GetProcess(),
1245 root1->current_frame_host()->GetSiteInstance()->GetProcess());
1246
1247 {
1248 SCOPED_TRACE("child_frame_node1");
1249 TestDefaultIsolationForFrame(
1250 child_frame_node1, default_isolated_url,
1251 /*expect_origin_agent_cluster=*/true,
1252 /*expect_requires_origin_keyed_process=*/true,
1253 /*expect_default_requires_origin_keyed_process=*/true);
1254 }
1255
1256 // Dynamically disable the feature to simulate the enterprise policy being
1257 // dynamically changed.
1258 browser_client_->SetShouldDisableOriginAgentClusterDefault(true);
1259
1260 // Create a second BrowsingInstance. This one will have default isolation with
1261 // origin_agent_cluster = false and requests_origin_keyed_process (by default)
1262 // false.
1263 Shell* shell2 = CreateBrowser();
1264 auto* web_contents2 = static_cast<WebContentsImpl*>(shell2->web_contents());
1265
1266 // Load a frame into the second BrowsingInstance.
1267 // This request also has no OriginAgentCluster header, but it should not get
1268 // OAC by default, nor request process-isolation.
1269 EXPECT_TRUE(NavigateToURL(shell2, test_url));
1270 FrameTreeNode* root2 = web_contents2->GetPrimaryFrameTree().root();
1271 FrameTreeNode* child_frame_node2 = root2->child_at(0);
1272 // We navigate to `default_isolated_url` again so that we're using the same
1273 // urls in both parts of the test, but we don't expect it to be isolated in
1274 // this BrowsingInstance.
1275 EXPECT_TRUE(
1276 NavigateToURLFromRenderer(child_frame_node2, default_isolated_url));
1277 EXPECT_EQ(
1278 child_frame_node2->current_frame_host()->GetSiteInstance()->GetProcess(),
1279 root2->current_frame_host()->GetSiteInstance()->GetProcess());
1280
1281 {
1282 SCOPED_TRACE("child_frame_node2");
1283 TestDefaultIsolationForFrame(
1284 child_frame_node2, default_isolated_url,
1285 /*expect_origin_agent_cluster=*/false,
1286 /*expect_requires_origin_keyed_process=*/false,
1287 /*expect_default_requires_origin_keyed_process=*/false);
1288 }
1289
1290 // We expect the default isolation to be different in the two
1291 // BrowsingInstances.
1292 EXPECT_NE(root1->current_frame_host()
1293 ->GetSiteInstance()
1294 ->GetIsolationContext()
1295 .default_isolation_state(),
1296 root2->current_frame_host()
1297 ->GetSiteInstance()
1298 ->GetIsolationContext()
1299 .default_isolation_state());
1300
1301 // Another navigation in root1 should respect the origin-keyed default used by
1302 // that BrowsingInstance and not the current site-keyed default.
1303 GURL default_isolated_url2(
1304 https_server()->GetURL("isolated.bar.com", "/title1.html"));
1305 EXPECT_TRUE(NavigateToURL(shell1, test_url));
1306 {
1307 SCOPED_TRACE("root1");
1308 TestDefaultIsolationForFrame(
1309 root1, default_isolated_url2,
1310 /*expect_origin_agent_cluster=*/true,
1311 /*expect_requires_origin_keyed_process=*/true,
1312 /*expect_default_requires_origin_keyed_process=*/true);
1313 }
1314}
1315
W. James MacLean64448dea2022-03-18 20:22:211316IN_PROC_BROWSER_TEST_F(OriginIsolationDefaultOACTest, Basic) {
1317 GURL test_url(https_server()->GetURL("foo.com",
1318 "/cross_site_iframe_factory.html?"
1319 "foo.com(foo.com)"));
1320 // We must load the origins to be isolated (or not) into a child frame so that
1321 // they all stay in the same BrowsingInstance, since the test relies on
1322 // knowing isolation history for the OriginAgentClusterEndResult::*But* cases.
1323 // In this test, the convention is:
1324 // foo.com is (implicitly) isolated,
1325 // isolated.foo.com is (explicitly) isolated,
1326 // isolated.bar.com is (implicitly) isolated, and
1327 // bar.com is (explicitly) not isolated.
1328 EXPECT_TRUE(NavigateToURL(shell(), test_url));
1329 EXPECT_EQ(2u, CollectAllRenderFrameHosts(shell()->web_contents()).size());
1330 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
1331 FrameTreeNode* child_frame_node = root->child_at(0);
1332
1333 // The first three scenarios should all get the isolation status they request
1334 // (the "And" cases).
1335 {
1336 // Explicitly request OriginAgentCluster via the header.
1337 SetHeaderValue("?1");
W. James MacLean04279f52022-03-21 16:33:561338 base::HistogramTester histograms;
W. James MacLean64448dea2022-03-18 20:22:211339 GURL isolated_suborigin_url(
1340 https_server()->GetURL("isolated.foo.com", "/isolate_origin"));
1341 EXPECT_TRUE(
1342 NavigateToURLFromRenderer(child_frame_node, isolated_suborigin_url));
1343 auto* site_instance =
1344 child_frame_node->current_frame_host()->GetSiteInstance();
1345 EXPECT_TRUE(DetermineOriginAgentClusterIsolation(site_instance,
1346 isolated_suborigin_url)
1347 .requires_origin_keyed_process());
W. James MacLean04279f52022-03-21 16:33:561348
1349 EXPECT_THAT(
1350 histograms.GetAllSamples("Navigation.OriginAgentCluster.Result"),
1351 testing::ElementsAre(base::Bucket(
1352 static_cast<int>(NavigationRequest::OriginAgentClusterEndResult::
1353 kExplicitlyRequestedAndOriginKeyed),
1354 1)));
W. James MacLean64448dea2022-03-18 20:22:211355 }
1356 {
1357 // Even though this request has no OriginAgentCluster header, it should get
1358 // OAC by default.
1359 SetHeaderValue("");
W. James MacLean04279f52022-03-21 16:33:561360 base::HistogramTester histograms;
W. James MacLean64448dea2022-03-18 20:22:211361 GURL default_isolated_url(
1362 https_server()->GetURL("isolated.bar.com", "/title1.html"));
1363 EXPECT_TRUE(
1364 NavigateToURLFromRenderer(child_frame_node, default_isolated_url));
1365
1366 auto* site_instance =
1367 child_frame_node->current_frame_host()->GetSiteInstance();
1368 OriginAgentClusterIsolationState isolation_state =
1369 DetermineOriginAgentClusterIsolation(site_instance,
1370 default_isolated_url);
1371 // TODO(wjmaclean): If OriginAgentCluster-by-default transitions to using
1372 // process-isolation at some future date, the second expectation below will
1373 // need to change to EXPECT_TRUE.
1374 EXPECT_TRUE(isolation_state.is_origin_agent_cluster());
1375 EXPECT_FALSE(isolation_state.requires_origin_keyed_process());
W. James MacLean04279f52022-03-21 16:33:561376
1377 EXPECT_THAT(
1378 histograms.GetAllSamples("Navigation.OriginAgentCluster.Result"),
1379 testing::ElementsAre(base::Bucket(
1380 static_cast<int>(NavigationRequest::OriginAgentClusterEndResult::
1381 kNotExplicitlyRequestedAndOriginKeyed),
1382 1)));
W. James MacLeanc07dc41b2022-07-25 18:52:161383 // Ensure that the implicit case did not do a global walk (which would be
1384 // inefficient), by noticing that a hypothetical request for non-isolation
1385 // of that origin in the same SiteInstance would succeed. That can only
1386 // happen if the implicit case was not recorded in the BrowsingInstance.
1387 OriginAgentClusterIsolationState hypothetical_isolation_request =
Camille Lamy5ce9b962025-08-08 12:10:451388 OriginAgentClusterIsolationState::CreateNonIsolatedByHeader();
W. James MacLeanc07dc41b2022-07-25 18:52:161389 OriginAgentClusterIsolationState hypothetical_isolation_state =
1390 ChildProcessSecurityPolicyImpl::GetInstance()
1391 ->DetermineOriginAgentClusterIsolation(
1392 site_instance->GetIsolationContext(),
1393 url::Origin::Create(default_isolated_url),
1394 hypothetical_isolation_request);
1395 EXPECT_FALSE(hypothetical_isolation_state.is_origin_agent_cluster());
W. James MacLean64448dea2022-03-18 20:22:211396 }
1397 {
1398 // The "isolate_origin" path in the url will force the test framework to
1399 // include the OriginAgentCluster header. Here we explicitly request not to
1400 // have OAC.
1401 SetHeaderValue("?0");
W. James MacLean04279f52022-03-21 16:33:561402 base::HistogramTester histograms;
W. James MacLean64448dea2022-03-18 20:22:211403 GURL explicit_non_isolated_url(
1404 https_server()->GetURL("bar.com", "/isolate_origin"));
1405 EXPECT_TRUE(
1406 NavigateToURLFromRenderer(child_frame_node, explicit_non_isolated_url));
1407 auto* site_instance =
1408 child_frame_node->current_frame_host()->GetSiteInstance();
1409
1410 EXPECT_FALSE(DetermineOriginAgentClusterIsolation(site_instance,
1411 explicit_non_isolated_url)
1412 .is_origin_agent_cluster());
W. James MacLean04279f52022-03-21 16:33:561413
1414 EXPECT_THAT(
1415 histograms.GetAllSamples("Navigation.OriginAgentCluster.Result"),
1416 testing::ElementsAre(base::Bucket(
1417 static_cast<int>(NavigationRequest::OriginAgentClusterEndResult::
1418 kExplicitlyNotRequestedAndNotOriginKeyed),
1419 1)));
W. James MacLean64448dea2022-03-18 20:22:211420 }
1421
1422 // The next three cases should all fail to get the isolation status they
1423 // request (the "But" cases). In these cases, URLs from origins we have
1424 // already visited in the BrowsingInstance return different OAC header values,
1425 // but are forced to stick with their earlier value rather than the newly
1426 // requested value.
1427 {
1428 // Even though the lack of a header would normally lead to default OAC
1429 // isolation, the previous explicitly non-isolated visit to this origin
1430 // means that this origin will remain not origin keyed.
1431 SetHeaderValue("");
W. James MacLean04279f52022-03-21 16:33:561432 base::HistogramTester histograms;
W. James MacLean64448dea2022-03-18 20:22:211433 GURL url(https_server()->GetURL("bar.com", "/title1.html"));
1434 EXPECT_TRUE(NavigateToURLFromRenderer(child_frame_node, url));
1435 auto* site_instance =
1436 child_frame_node->current_frame_host()->GetSiteInstance();
1437
1438 EXPECT_FALSE(DetermineOriginAgentClusterIsolation(site_instance, url)
1439 .is_origin_agent_cluster());
W. James MacLean04279f52022-03-21 16:33:561440
1441 EXPECT_THAT(
1442 histograms.GetAllSamples("Navigation.OriginAgentCluster.Result"),
1443 testing::ElementsAre(base::Bucket(
1444 static_cast<int>(NavigationRequest::OriginAgentClusterEndResult::
1445 kNotExplicitlyRequestedButNotOriginKeyed),
1446 1)));
W. James MacLean64448dea2022-03-18 20:22:211447 }
1448
1449 {
1450 // An explicit opt-out for isolated.bar.com should not be granted given the
1451 // previous default-opt-in above.
1452 SetHeaderValue("?0");
W. James MacLean04279f52022-03-21 16:33:561453 base::HistogramTester histograms;
W. James MacLean64448dea2022-03-18 20:22:211454 GURL explicit_non_isolated_url(
1455 https_server()->GetURL("isolated.bar.com", "/isolate_origin"));
1456 EXPECT_TRUE(
1457 NavigateToURLFromRenderer(child_frame_node, explicit_non_isolated_url));
1458 auto* site_instance =
1459 child_frame_node->current_frame_host()->GetSiteInstance();
1460
1461 OriginAgentClusterIsolationState isolation_state =
1462 DetermineOriginAgentClusterIsolation(site_instance,
1463 explicit_non_isolated_url);
1464 EXPECT_TRUE(isolation_state.is_origin_agent_cluster());
1465 EXPECT_FALSE(isolation_state.requires_origin_keyed_process());
W. James MacLean04279f52022-03-21 16:33:561466
1467 EXPECT_THAT(
1468 histograms.GetAllSamples("Navigation.OriginAgentCluster.Result"),
1469 testing::ElementsAre(base::Bucket(
1470 static_cast<int>(NavigationRequest::OriginAgentClusterEndResult::
1471 kExplicitlyNotRequestedButOriginKeyed),
1472 1)));
W. James MacLean64448dea2022-03-18 20:22:211473 }
1474 {
1475 // Verify that we don't explicitly opt-in an origin that was explicitly
1476 // opted-out.
1477 SetHeaderValue("?1");
W. James MacLean04279f52022-03-21 16:33:561478 base::HistogramTester histograms;
W. James MacLean64448dea2022-03-18 20:22:211479 GURL explicit_isolated_url(
1480 https_server()->GetURL("bar.com", "/isolate_origin"));
1481 EXPECT_TRUE(
1482 NavigateToURLFromRenderer(child_frame_node, explicit_isolated_url));
1483 auto* site_instance =
1484 child_frame_node->current_frame_host()->GetSiteInstance();
1485
1486 EXPECT_FALSE(DetermineOriginAgentClusterIsolation(site_instance,
1487 explicit_isolated_url)
1488 .is_origin_agent_cluster());
W. James MacLean04279f52022-03-21 16:33:561489
1490 EXPECT_THAT(
1491 histograms.GetAllSamples("Navigation.OriginAgentCluster.Result"),
1492 testing::ElementsAre(base::Bucket(
1493 static_cast<int>(NavigationRequest::OriginAgentClusterEndResult::
1494 kExplicitlyRequestedButNotOriginKeyed),
1495 1)));
W. James MacLean64448dea2022-03-18 20:22:211496 }
1497}
1498
Domenic Denicola4d1145d2020-10-29 15:05:331499// These tests ensure that non-HTTPS secure contexts (see
1500// https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy) are
1501// able to use origin isolation.
1502IN_PROC_BROWSER_TEST_F(OriginIsolationOptInHttpServerHeaderTest, Localhost) {
1503 GURL url(embedded_test_server()->GetURL("localhost", "/"));
1504 url::Origin origin(url::Origin::Create(url));
1505
W. James MacLean7f76c2202021-11-15 16:27:491506 EXPECT_FALSE(ShouldOriginGetOptInProcessIsolation(origin));
Domenic Denicola4d1145d2020-10-29 15:05:331507 EXPECT_TRUE(NavigateToURL(shell(), url));
W. James MacLean7f76c2202021-11-15 16:27:491508 EXPECT_TRUE(ShouldOriginGetOptInProcessIsolation(origin));
Domenic Denicola4d1145d2020-10-29 15:05:331509}
1510
1511IN_PROC_BROWSER_TEST_F(OriginIsolationOptInHttpServerHeaderTest, DotLocalhost) {
1512 GURL url(embedded_test_server()->GetURL("test.localhost", "/"));
1513 url::Origin origin(url::Origin::Create(url));
1514
W. James MacLean7f76c2202021-11-15 16:27:491515 EXPECT_FALSE(ShouldOriginGetOptInProcessIsolation(origin));
Domenic Denicola4d1145d2020-10-29 15:05:331516 EXPECT_TRUE(NavigateToURL(shell(), url));
W. James MacLean7f76c2202021-11-15 16:27:491517 EXPECT_TRUE(ShouldOriginGetOptInProcessIsolation(origin));
Domenic Denicola4d1145d2020-10-29 15:05:331518}
1519
1520IN_PROC_BROWSER_TEST_F(OriginIsolationOptInHttpServerHeaderTest,
1521 OneTwentySeven) {
1522 GURL url(embedded_test_server()->GetURL("127.0.0.1", "/"));
1523 url::Origin origin(url::Origin::Create(url));
1524
W. James MacLean7f76c2202021-11-15 16:27:491525 EXPECT_FALSE(ShouldOriginGetOptInProcessIsolation(origin));
Domenic Denicola4d1145d2020-10-29 15:05:331526 EXPECT_TRUE(NavigateToURL(shell(), url));
W. James MacLean7f76c2202021-11-15 16:27:491527 EXPECT_TRUE(ShouldOriginGetOptInProcessIsolation(origin));
Domenic Denicola4d1145d2020-10-29 15:05:331528}
1529
W. James MacLeanc6dc86c2021-08-12 13:58:371530// Two tests for basic OAC operation w.r.t. prerendering FrameTrees.
1531
1532// Basic test to make sure an origin opting-in in a primary FrameTree
1533// triggers registration of a non-opting-origin in an existing prerendering
1534// Frametree.
1535IN_PROC_BROWSER_TEST_F(OriginIsolationPrerenderOptInHeaderTest,
W. James MacLeancb7907e62022-04-08 15:48:451536 SimplePrerenderSubOriginIsolationTest) {
W. James MacLeanc6dc86c2021-08-12 13:58:371537 GURL test_url(https_server()->GetURL("foo.com",
1538 "/cross_site_iframe_factory.html?"
1539 "foo.com(foo.com)"));
1540 // Navigate primary tab to a non-isolated origin.
1541 EXPECT_TRUE(NavigateToURL(shell(), test_url));
Dave Tapuska4eea4112021-09-16 15:49:211542 EXPECT_EQ(2u, CollectAllRenderFrameHosts(shell()->web_contents()).size());
Carlos Caballero15caeeb2021-10-27 09:57:551543 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
W. James MacLeanc6dc86c2021-08-12 13:58:371544 FrameTreeNode* child_frame_node = root->child_at(0);
1545
1546 // Create prerender tab, load non-isolated "a.foo.com".
1547 Shell* prerender_tab = CreateBrowser();
1548 EXPECT_TRUE(NavigateToURL(prerender_tab, GURL(https_server()->GetURL(
1549 "a.foo.com", "/title1.html"))));
1550 auto* prerender_web_contents =
1551 static_cast<WebContentsImpl*>(prerender_tab->web_contents());
1552 set_prerender_web_contents(prerender_web_contents);
1553 GURL non_isolated_origin_url(
1554 https_server()->GetURL("a.foo.com", "/title2.html"));
1555
Avi Drissman13d20e92024-09-05 16:56:301556 FrameTreeNodeId host_id =
1557 prerender_helper_.AddPrerender(non_isolated_origin_url);
W. James MacLeanc6dc86c2021-08-12 13:58:371558
1559 // In primary tab, navigate to an isolated origin.
1560 SetHeaderValue("?1");
1561 GURL isolated_suborigin_url(
1562 https_server()->GetURL("a.foo.com", "/isolate_origin"));
1563 EXPECT_TRUE(
1564 NavigateToURLFromRenderer(child_frame_node, isolated_suborigin_url));
1565 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
1566 child_frame_node->current_frame_host()->GetSiteInstance());
1567 EXPECT_TRUE(child_frame_node->current_frame_host()
1568 ->GetSiteInstance()
1569 ->RequiresDedicatedProcess());
1570 EXPECT_TRUE(child_frame_node->current_frame_host()
1571 ->GetSiteInstance()
1572 ->GetSiteInfo()
W. James MacLean7f76c2202021-11-15 16:27:491573 .requires_origin_keyed_process());
W. James MacLeanc6dc86c2021-08-12 13:58:371574
1575 // Verify in prerender tab that "a.foo.com" is registered as a non-isolated
1576 // origin. We must get the SiteInstance() to test from the
1577 // PrerenderedMainFrameHost() to make sure the opt-out registration has
1578 // propagated to the right place.
1579 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
1580 auto* prerender_site_instance_impl = static_cast<SiteInstanceImpl*>(
1581 prerender_helper_.GetPrerenderedMainFrameHost(host_id)
1582 ->GetSiteInstance());
W. James MacLean7f76c2202021-11-15 16:27:491583 EXPECT_FALSE(policy
W. James MacLeand42fa812021-11-18 22:59:261584 ->DetermineOriginAgentClusterIsolation(
W. James MacLean7f76c2202021-11-15 16:27:491585 prerender_site_instance_impl->GetIsolationContext(),
1586 url::Origin::Create(non_isolated_origin_url),
1587 MakeOACIsolationState(true))
1588 .requires_origin_keyed_process());
W. James MacLeanc6dc86c2021-08-12 13:58:371589
1590 // Activate the prerendered page and confirm the non-isolated origin remains
1591 // non-isolated.
1592 prerender_helper_.NavigatePrimaryPage(non_isolated_origin_url);
1593 auto* new_prerender_site_instance_impl = static_cast<SiteInstanceImpl*>(
1594 prerender_tab->web_contents()->GetSiteInstance());
1595 EXPECT_EQ(prerender_site_instance_impl, new_prerender_site_instance_impl);
W. James MacLean7f76c2202021-11-15 16:27:491596 EXPECT_FALSE(policy
W. James MacLeand42fa812021-11-18 22:59:261597 ->DetermineOriginAgentClusterIsolation(
W. James MacLean7f76c2202021-11-15 16:27:491598 new_prerender_site_instance_impl->GetIsolationContext(),
1599 url::Origin::Create(non_isolated_origin_url),
1600 MakeOACIsolationState(true))
1601 .requires_origin_keyed_process());
1602 EXPECT_FALSE(new_prerender_site_instance_impl->GetSiteInfo()
1603 .requires_origin_keyed_process());
W. James MacLeanc6dc86c2021-08-12 13:58:371604 EXPECT_TRUE(new_prerender_site_instance_impl->GetSiteURL() ==
1605 GURL("https://foo.com") ||
1606 new_prerender_site_instance_impl->IsDefaultSiteInstance());
1607}
1608
1609// Basic test to make sure an origin opting-in in a prerendering FrameTree
1610// triggers registration of a non-opting-origin in an existing primary
1611// Frametree.
1612IN_PROC_BROWSER_TEST_F(OriginIsolationPrerenderOptInHeaderTest,
1613 SimplePrerenderSubOriginIsolationTest2) {
1614 GURL test_url(https_server()->GetURL("foo.com",
1615 "/cross_site_iframe_factory.html?"
1616 "foo.com(foo.com)"));
1617 EXPECT_TRUE(NavigateToURL(shell(), test_url));
Dave Tapuska4eea4112021-09-16 15:49:211618 EXPECT_EQ(2u, CollectAllRenderFrameHosts(shell()->web_contents()).size());
Carlos Caballero15caeeb2021-10-27 09:57:551619 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
W. James MacLeanc6dc86c2021-08-12 13:58:371620 FrameTreeNode* child_frame_node = root->child_at(0);
1621 // Navigate child frame to a non-isolated origin "a.foo.com".
1622 GURL non_isolated_suborigin_url(
1623 https_server()->GetURL("a.foo.com", "/title1.html"));
1624 EXPECT_TRUE(
1625 NavigateToURLFromRenderer(child_frame_node, non_isolated_suborigin_url));
1626 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
1627 child_frame_node->current_frame_host()->GetSiteInstance());
1628
1629 // Create prerender tab, load isolated "a.foo.com".
1630 Shell* prerender_tab = CreateBrowser();
1631 EXPECT_TRUE(NavigateToURL(prerender_tab, GURL(https_server()->GetURL(
1632 "a.foo.com", "/title1.html"))));
1633 auto* prerender_web_contents =
1634 static_cast<WebContentsImpl*>(prerender_tab->web_contents());
1635 set_prerender_web_contents(prerender_web_contents);
1636 SetHeaderValue("?1");
1637 GURL isolated_origin_url(
1638 https_server()->GetURL("a.foo.com", "/isolate_origin"));
1639
Avi Drissman13d20e92024-09-05 16:56:301640 FrameTreeNodeId host_id = prerender_helper_.AddPrerender(isolated_origin_url);
W. James MacLeanc6dc86c2021-08-12 13:58:371641
1642 // Verify origin is isolated in the prerender IsolationContext.
1643 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
1644 auto* prerender_site_instance_impl = static_cast<SiteInstanceImpl*>(
1645 prerender_helper_.GetPrerenderedMainFrameHost(host_id)
1646 ->GetSiteInstance());
W. James MacLean7f76c2202021-11-15 16:27:491647 EXPECT_TRUE(policy
W. James MacLeand42fa812021-11-18 22:59:261648 ->DetermineOriginAgentClusterIsolation(
W. James MacLean7f76c2202021-11-15 16:27:491649 prerender_site_instance_impl->GetIsolationContext(),
1650 url::Origin::Create(isolated_origin_url),
1651 MakeOACIsolationState(false))
1652 .requires_origin_keyed_process());
W. James MacLeanc6dc86c2021-08-12 13:58:371653 EXPECT_TRUE(prerender_site_instance_impl->RequiresDedicatedProcess());
W. James MacLean7f76c2202021-11-15 16:27:491654 EXPECT_TRUE(prerender_site_instance_impl->GetSiteInfo()
1655 .requires_origin_keyed_process());
W. James MacLeanc6dc86c2021-08-12 13:58:371656
1657 // Verify in original tab that "a.foo.com" is now registered as a non-isolated
1658 // origin.
1659 auto* primary_site_instance_impl = static_cast<SiteInstanceImpl*>(
1660 shell()->web_contents()->GetSiteInstance());
W. James MacLean7f76c2202021-11-15 16:27:491661 EXPECT_FALSE(policy
W. James MacLeand42fa812021-11-18 22:59:261662 ->DetermineOriginAgentClusterIsolation(
W. James MacLean7f76c2202021-11-15 16:27:491663 primary_site_instance_impl->GetIsolationContext(),
1664 url::Origin::Create(isolated_origin_url),
1665 MakeOACIsolationState(true))
1666 .requires_origin_keyed_process());
W. James MacLeanc6dc86c2021-08-12 13:58:371667
1668 // Activate the prerendered page and confirm the isolated origin remains
1669 // isolated.
1670 prerender_helper_.NavigatePrimaryPage(isolated_origin_url);
1671 auto* new_prerender_site_instance_impl = static_cast<SiteInstanceImpl*>(
1672 prerender_tab->web_contents()->GetSiteInstance());
1673 EXPECT_EQ(prerender_site_instance_impl, new_prerender_site_instance_impl);
W. James MacLean7f76c2202021-11-15 16:27:491674 EXPECT_TRUE(policy
W. James MacLeand42fa812021-11-18 22:59:261675 ->DetermineOriginAgentClusterIsolation(
W. James MacLean7f76c2202021-11-15 16:27:491676 new_prerender_site_instance_impl->GetIsolationContext(),
1677 url::Origin::Create(isolated_origin_url),
1678 MakeOACIsolationState(false))
1679 .requires_origin_keyed_process());
W. James MacLeanc6dc86c2021-08-12 13:58:371680 EXPECT_TRUE(prerender_site_instance_impl->RequiresDedicatedProcess());
W. James MacLean7f76c2202021-11-15 16:27:491681 EXPECT_TRUE(new_prerender_site_instance_impl->GetSiteInfo()
1682 .requires_origin_keyed_process());
W. James MacLeanc6dc86c2021-08-12 13:58:371683}
1684
Domenic Denicola14103152020-04-03 19:40:451685// Further tests deep-dive into various scenarios for the isolation opt-ins.
Domenic Denicola14103152020-04-03 19:40:451686
Domenic Denicola7dbd3d12021-01-08 21:26:561687// In this test the sub-origin is isolated because the header requests it. It
1688// will have a different site instance than the main frame.
1689IN_PROC_BROWSER_TEST_F(OriginIsolationOptInHeaderTest,
Domenic Denicola14103152020-04-03 19:40:451690 SimpleSubOriginIsolationTest) {
W. James MacLeana485f0e2021-01-29 17:18:051691 base::HistogramTester histograms;
Domenic Denicola7dbd3d12021-01-08 21:26:561692 SetHeaderValue("?1");
W. James MacLean64ddbcc2020-01-24 22:34:221693 // Start off with an a(a) page, then navigate the subframe to an isolated sub
1694 // origin.
1695 GURL test_url(https_server()->GetURL("foo.com",
1696 "/cross_site_iframe_factory.html?"
1697 "foo.com(foo.com)"));
W. James MacLean1c40862c2020-04-27 21:05:571698 GURL isolated_suborigin_url(
1699 https_server()->GetURL("isolated.foo.com", "/isolate_origin"));
W. James MacLean222a2472020-08-14 22:00:221700 GURL origin_url = url::Origin::Create(isolated_suborigin_url).GetURL();
Robbie McElrath7d4bd852021-07-24 04:02:191701 BrowserContext* browser_context = web_contents()->GetBrowserContext();
Robbie McElratheae661e2023-08-10 19:05:281702 auto expected_isolated_suborigin_lock = ProcessLock::FromSiteInfo(SiteInfo(
Camille Lamy52a51202025-07-29 14:16:121703 AgentClusterKey::CreateOriginKeyed(url::Origin::Create(origin_url)),
Robbie McElratheae661e2023-08-10 19:05:281704 /*site_url=*/origin_url,
1705 /*process_lock_url=*/origin_url,
1706 /*requires_origin_keyed_process=*/true,
Camille Lamy5ce9b962025-08-08 12:10:451707 AgentClusterKey::OACStatus::kOriginKeyedByHeader,
Robbie McElratheae661e2023-08-10 19:05:281708 /*is_sandboxed=*/false, UrlInfo::kInvalidUniqueSandboxId,
1709 StoragePartitionConfig::CreateDefault(browser_context),
1710 WebExposedIsolationInfo::CreateNonIsolated(),
1711 WebExposedIsolationLevel::kNotIsolated, /*is_guest=*/false,
1712 /*does_site_request_dedicated_process_for_coop=*/false,
Ellyc737a6302024-08-19 15:30:511713 /*is_jit_disabled=*/false, /*are_v8_optimizations_disabled=*/false,
Camille Lamy52a51202025-07-29 14:16:121714 /*is_pdf=*/false, /*is_fenced=*/false));
W. James MacLean64ddbcc2020-01-24 22:34:221715 EXPECT_TRUE(NavigateToURL(shell(), test_url));
Dave Tapuska4eea4112021-09-16 15:49:211716 EXPECT_EQ(2u, CollectAllRenderFrameHosts(shell()->web_contents()).size());
W. James MacLean64ddbcc2020-01-24 22:34:221717
Carlos Caballero15caeeb2021-10-27 09:57:551718 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
W. James MacLean64ddbcc2020-01-24 22:34:221719 FrameTreeNode* child_frame_node = root->child_at(0);
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201720 EXPECT_TRUE(
1721 NavigateToURLFromRenderer(child_frame_node, isolated_suborigin_url));
W. James MacLean64ddbcc2020-01-24 22:34:221722 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
1723 child_frame_node->current_frame_host()->GetSiteInstance());
1724 EXPECT_TRUE(child_frame_node->current_frame_host()
1725 ->GetSiteInstance()
1726 ->RequiresDedicatedProcess());
1727 GURL expected_isolated_sub_origin =
Aaron Colwell80f85bb2020-05-19 01:55:061728 url::Origin::Create(isolated_suborigin_url).GetURL();
W. James MacLean64ddbcc2020-01-24 22:34:221729 EXPECT_EQ(
1730 expected_isolated_sub_origin,
1731 child_frame_node->current_frame_host()->GetSiteInstance()->GetSiteURL());
W. James MacLeane84fa112020-07-14 17:25:541732 EXPECT_EQ(expected_isolated_suborigin_lock,
Sharon Yang2c077a72021-11-30 02:27:581733 ProcessLock::FromSiteInfo(child_frame_node->current_frame_host()
1734 ->GetSiteInstance()
1735 ->GetSiteInfo()));
Sharon Yang57481e52021-12-01 00:05:221736 EXPECT_EQ(
1737 ProcessLock::FromSiteInfo(child_frame_node->current_frame_host()
1738 ->GetSiteInstance()
1739 ->GetSiteInfo()),
1740 child_frame_node->current_frame_host()->GetProcess()->GetProcessLock());
W. James MacLeana485f0e2021-01-29 17:18:051741
1742 EXPECT_THAT(
1743 histograms.GetAllSamples("Navigation.OriginAgentCluster.Result"),
1744 testing::ElementsAre(
1745 base::Bucket(
1746 static_cast<int>(NavigationRequest::OriginAgentClusterEndResult::
1747 kNotRequestedAndNotOriginKeyed),
1748 2),
1749 base::Bucket(
1750 static_cast<int>(NavigationRequest::OriginAgentClusterEndResult::
1751 kRequestedAndOriginKeyed),
1752 1)));
W. James MacLean64ddbcc2020-01-24 22:34:221753}
1754
Alex Moshchuked2376d2022-05-05 22:38:461755// Check that two same-site Origin-Agent-Cluster subframes in unrelated windows
1756// obey the subframe process reuse policy.
1757IN_PROC_BROWSER_TEST_F(OriginIsolationOptInHeaderTest,
1758 OriginAgentClusterProcessReuse) {
1759 SetHeaderValue("?1");
1760 // Start off with an a(a) page, then navigate the subframe to an isolated
1761 // suborigin.
1762 GURL test_url(https_server()->GetURL("foo.com",
1763 "/cross_site_iframe_factory.html?"
1764 "foo.com(foo.com)"));
1765 GURL isolated_suborigin_url(
1766 https_server()->GetURL("isolated.foo.com", "/isolate_origin"));
1767 EXPECT_TRUE(NavigateToURL(shell(), test_url));
1768
1769 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
1770 FrameTreeNode* child = root->child_at(0);
1771 EXPECT_TRUE(NavigateToURLFromRenderer(child, isolated_suborigin_url));
1772 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
1773 child->current_frame_host()->GetSiteInstance());
1774 EXPECT_TRUE(child->current_frame_host()
1775 ->GetSiteInstance()
1776 ->GetSiteInfo()
1777 .requires_origin_keyed_process());
1778
1779 // Open an unrelated window and set up the same frame hierarchy there.
1780 Shell* new_shell = CreateBrowser();
1781 EXPECT_TRUE(NavigateToURL(new_shell, test_url));
1782 FrameTreeNode* new_root =
1783 static_cast<WebContentsImpl*>(new_shell->web_contents())
1784 ->GetPrimaryFrameTree()
1785 .root();
1786 FrameTreeNode* new_child = new_root->child_at(0);
1787 EXPECT_TRUE(NavigateToURLFromRenderer(new_child, isolated_suborigin_url));
1788 EXPECT_NE(new_root->current_frame_host()->GetSiteInstance(),
1789 new_child->current_frame_host()->GetSiteInstance());
1790
1791 // Even though the two subframes should be in different BrowsingInstances,
1792 // they should share the same process due to the subframe process reuse
1793 // policy.
1794 EXPECT_FALSE(
1795 child->current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance(
1796 new_child->current_frame_host()->GetSiteInstance()));
1797 EXPECT_EQ(child->current_frame_host()->GetProcess(),
1798 new_child->current_frame_host()->GetProcess());
1799}
1800
W. James MacLean92e39c82021-02-25 23:27:341801// In this test the sub-origin is isolated because the header requests it. It
1802// will have the same site instance as the main frame, and it will be in the
1803// same process.
1804IN_PROC_BROWSER_TEST_F(SameProcessOriginIsolationOptInHeaderTest,
1805 SimpleSubOriginIsolationTest) {
1806 base::HistogramTester histograms;
1807 SetHeaderValue("?1");
1808 // Start off with an a(a) page, then navigate the subframe to an isolated sub
1809 // origin.
1810 GURL test_url(https_server()->GetURL("foo.com",
1811 "/cross_site_iframe_factory.html?"
1812 "foo.com(foo.com)"));
1813 GURL isolated_suborigin_url(
1814 https_server()->GetURL("isolated.foo.com", "/isolate_origin"));
1815 GURL origin_url = url::Origin::Create(isolated_suborigin_url).GetURL();
1816 EXPECT_FALSE(
1817 SiteIsolationPolicy::IsProcessIsolationForOriginAgentClusterEnabled());
1818 EXPECT_TRUE(NavigateToURL(shell(), test_url));
Dave Tapuska4eea4112021-09-16 15:49:211819 EXPECT_EQ(2u, CollectAllRenderFrameHosts(shell()->web_contents()).size());
W. James MacLean92e39c82021-02-25 23:27:341820
Carlos Caballero15caeeb2021-10-27 09:57:551821 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
W. James MacLean92e39c82021-02-25 23:27:341822 FrameTreeNode* child_frame_node = root->child_at(0);
1823 EXPECT_TRUE(
1824 NavigateToURLFromRenderer(child_frame_node, isolated_suborigin_url));
1825 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
1826 child_frame_node->current_frame_host()->GetSiteInstance());
1827 EXPECT_FALSE(child_frame_node->current_frame_host()
1828 ->GetSiteInstance()
1829 ->RequiresDedicatedProcess());
1830 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
W. James MacLean7f76c2202021-11-15 16:27:491831 EXPECT_TRUE(policy
W. James MacLeand42fa812021-11-18 22:59:261832 ->DetermineOriginAgentClusterIsolation(
W. James MacLean7f76c2202021-11-15 16:27:491833 root->current_frame_host()
1834 ->GetSiteInstance()
1835 ->GetIsolationContext(),
1836 url::Origin::Create(isolated_suborigin_url),
1837 MakeOACIsolationState(false))
1838 .is_origin_agent_cluster());
1839 EXPECT_FALSE(policy
W. James MacLeand42fa812021-11-18 22:59:261840 ->DetermineOriginAgentClusterIsolation(
W. James MacLean7f76c2202021-11-15 16:27:491841 root->current_frame_host()
1842 ->GetSiteInstance()
1843 ->GetIsolationContext(),
1844 url::Origin::Create(isolated_suborigin_url),
1845 MakeOACIsolationState(false))
1846 .requires_origin_keyed_process());
W. James MacLeanc07dc41b2022-07-25 18:52:161847 EXPECT_TRUE(policy->HasOriginEverRequestedOriginAgentClusterValue(
W. James MacLean92e39c82021-02-25 23:27:341848 web_contents()->GetBrowserContext(),
1849 url::Origin::Create(isolated_suborigin_url)));
1850
1851 EXPECT_THAT(
1852 histograms.GetAllSamples("Navigation.OriginAgentCluster.Result"),
1853 testing::ElementsAre(
1854 base::Bucket(
1855 static_cast<int>(NavigationRequest::OriginAgentClusterEndResult::
1856 kNotRequestedAndNotOriginKeyed),
1857 2),
1858 base::Bucket(
1859 static_cast<int>(NavigationRequest::OriginAgentClusterEndResult::
1860 kRequestedAndOriginKeyed),
1861 1)));
1862}
1863
W. James MacLean40304612021-08-25 15:25:581864// This test is *nearly* the same as SameProcessOriginIsolationOptInHeaderTest.
1865// SimpleSubOriginIsolationTest, but here we have command-line isolated foo.com
1866// so it will be in a site instance with a non-empty ProcessLock. But the
1867// same-process OAC isolated.foo.com will still be in the same SiteInstance,
1868// and checks on the expected ProcessLock for isolated.foo.com should pass,
1869// i.e. it should be the same as for the foo.com process.
1870IN_PROC_BROWSER_TEST_F(
1871 SameProcessOriginIsolationOptInHeaderWithIsolatedOriginTest,
1872 SimpleSubOriginIsolationTest) {
1873 base::HistogramTester histograms;
1874 SetHeaderValue("?1");
1875 // Start off with a foo(foo) page, then navigate the subframe to an isolated
1876 // sub origin. foo.com is isolated from the command line.
1877 GURL test_url(https_server()->GetURL("foo.com",
1878 "/cross_site_iframe_factory.html?"
1879 "foo.com(foo.com)"));
1880 GURL isolated_suborigin_url(
1881 https_server()->GetURL("isolated.foo.com", "/isolate_origin"));
1882 GURL origin_url = url::Origin::Create(isolated_suborigin_url).GetURL();
1883 EXPECT_FALSE(
1884 SiteIsolationPolicy::IsProcessIsolationForOriginAgentClusterEnabled());
1885 EXPECT_TRUE(NavigateToURL(shell(), test_url));
Dave Tapuska4eea4112021-09-16 15:49:211886 EXPECT_EQ(2u, CollectAllRenderFrameHosts(shell()->web_contents()).size());
W. James MacLean40304612021-08-25 15:25:581887
Carlos Caballero15caeeb2021-10-27 09:57:551888 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
W. James MacLean40304612021-08-25 15:25:581889 FrameTreeNode* child_frame_node = root->child_at(0);
1890 EXPECT_TRUE(
1891 NavigateToURLFromRenderer(child_frame_node, isolated_suborigin_url));
1892 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
1893 child_frame_node->current_frame_host()->GetSiteInstance());
1894 EXPECT_TRUE(root->current_frame_host()
1895 ->GetSiteInstance()
1896 ->RequiresDedicatedProcess());
1897 EXPECT_TRUE(child_frame_node->current_frame_host()
1898 ->GetSiteInstance()
1899 ->RequiresDedicatedProcess());
Sharon Yang2c077a72021-11-30 02:27:581900 ProcessLock root_process_lock = ProcessLock::FromSiteInfo(
1901 root->current_frame_host()->GetSiteInstance()->GetSiteInfo());
W. James MacLean40304612021-08-25 15:25:581902 EXPECT_TRUE(root_process_lock.is_locked_to_site());
1903 EXPECT_EQ(root_process_lock.lock_url(), GURL("https://foo.com/"));
1904 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
W. James MacLean7f76c2202021-11-15 16:27:491905 EXPECT_TRUE(policy
W. James MacLeand42fa812021-11-18 22:59:261906 ->DetermineOriginAgentClusterIsolation(
W. James MacLean7f76c2202021-11-15 16:27:491907 root->current_frame_host()
1908 ->GetSiteInstance()
1909 ->GetIsolationContext(),
1910 url::Origin::Create(isolated_suborigin_url),
1911 MakeOACIsolationState(false))
1912 .is_origin_agent_cluster());
1913 EXPECT_FALSE(policy
W. James MacLeand42fa812021-11-18 22:59:261914 ->DetermineOriginAgentClusterIsolation(
W. James MacLean7f76c2202021-11-15 16:27:491915 root->current_frame_host()
1916 ->GetSiteInstance()
1917 ->GetIsolationContext(),
1918 url::Origin::Create(isolated_suborigin_url),
1919 MakeOACIsolationState(false))
1920 .requires_origin_keyed_process());
W. James MacLeanc07dc41b2022-07-25 18:52:161921 EXPECT_TRUE(policy->HasOriginEverRequestedOriginAgentClusterValue(
W. James MacLean40304612021-08-25 15:25:581922 web_contents()->GetBrowserContext(),
1923 url::Origin::Create(isolated_suborigin_url)));
1924
1925 EXPECT_THAT(
1926 histograms.GetAllSamples("Navigation.OriginAgentCluster.Result"),
1927 testing::ElementsAre(
1928 base::Bucket(
1929 static_cast<int>(NavigationRequest::OriginAgentClusterEndResult::
1930 kNotRequestedAndNotOriginKeyed),
1931 2),
1932 base::Bucket(
1933 static_cast<int>(NavigationRequest::OriginAgentClusterEndResult::
1934 kRequestedAndOriginKeyed),
1935 1)));
1936}
1937
Daniel Cheng4191131a2021-04-21 22:01:451938// Verify OAC is calculated using the base URL when using LoadDataWithBaseURL()
1939// (analogous to Android WebView's loadDataWithBaseURL()) when the actual site
1940// does not specify an Origin-Agent-Cluster value.
1941IN_PROC_BROWSER_TEST_F(SameProcessOriginIsolationOptInHeaderTest,
1942 LoadDataWithBaseURLNoOAC) {
1943 const GURL test_url = https_server()->GetURL("foo.com", "/title1.html");
1944
1945 TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
1946 shell()->LoadDataWithBaseURL(
1947 test_url, "<!DOCTYPE html><html><body></body></html>", test_url);
1948 navigation_observer.Wait();
1949
1950 // Even though this internally navigates to a data: URL (which would imply
1951 // `window.originAgentCluster === true`, the base URL should be used for the
1952 // OAC calculation.
1953 EXPECT_EQ(false, EvalJs(shell(), "window.originAgentCluster"));
1954 EXPECT_TRUE(ExecJs(
1955 shell(), "document.body.appendChild(document.createElement('iframe'))"));
1956
1957 EXPECT_TRUE(NavigateToURLFromRenderer(
Dave Tapuska327c06c92022-06-13 20:31:511958 ChildFrameAt(web_contents()->GetPrimaryMainFrame(), 0), test_url));
1959 EXPECT_EQ(false,
1960 EvalJs(ChildFrameAt(web_contents()->GetPrimaryMainFrame(), 0),
1961 "window.originAgentCluster"));
Daniel Cheng4191131a2021-04-21 22:01:451962
1963 // If OAC is incorrectly calculated for `LoadDataWithBaseURL()`, this will
1964 // fail the access checks in Blink because the two browsing contexts will be
1965 // treated as cross-origin.
1966 EXPECT_EQ("This page has no title.\n\n",
1967 EvalJs(shell(), "window[0].document.body.textContent"));
1968}
1969
1970// Verify OAC is calculated using the base URL when using LoadDataWithBaseURL()
1971// (analogous to Android WebView's loadDataWithBaseURL()). Unlike the previous
1972// test, the actual site specifies an Origin-Agent-Cluster value, which should
1973// be ignored.
1974IN_PROC_BROWSER_TEST_F(SameProcessOriginIsolationOptInHeaderTest,
1975 LoadDataWithBaseURLWithOAC) {
1976 const GURL test_url = https_server()->GetURL("foo.com", "/isolate_origin");
1977 SetHeaderValue("?1");
1978
1979 // `tab2` and `shell()` will be in separate browsing instances. As an
1980 // optimization, browsing instances only track OAC consistency if an origin
1981 // has ever sent OAC headers. Once an origin has sent OAC headers, this is
1982 // tracked globally.
1983 //
1984 // This navigation marks "foo.com" as having sent OAC headers. This is
1985 // important to validate that `LoadDataWithBaseURL()` uses the origin
1986 // calculated from the base URL to update the non-isolated origin list in
1987 // `shell()`'s browsing instance. If this is not done correctly, then loading
1988 // "foo.com/isolate_origin" in the subframe will incorrectly use OAC in the
1989 // subframe, which will be inconsistent with the main frame loaded via
1990 // `LoadDataWithBaseURL()`.
1991 Shell* tab2 = CreateBrowser();
1992 EXPECT_TRUE(NavigateToURL(tab2, test_url));
1993
1994 TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
1995 shell()->LoadDataWithBaseURL(
1996 test_url, "<!DOCTYPE html><html><body></body></html>", test_url);
1997 navigation_observer.Wait();
1998
1999 // Even though this internally navigates to a data: URL (which would imply
2000 // `window.originAgentCluster === true`, the base URL should be used for the
2001 // OAC calculation.
2002 EXPECT_EQ(false, EvalJs(shell(), "window.originAgentCluster"));
2003 EXPECT_TRUE(ExecJs(
2004 shell(), "document.body.appendChild(document.createElement('iframe'))"));
2005
2006 // Even though this navigation sets the OAC header value, it should be
2007 // ignored, since the SiteInstance for foo.com is already site-keyed.
2008 EXPECT_TRUE(NavigateToURLFromRenderer(
Dave Tapuska327c06c92022-06-13 20:31:512009 ChildFrameAt(web_contents()->GetPrimaryMainFrame(), 0), test_url));
2010 EXPECT_EQ(false,
2011 EvalJs(ChildFrameAt(web_contents()->GetPrimaryMainFrame(), 0),
2012 "window.originAgentCluster"));
Daniel Cheng4191131a2021-04-21 22:01:452013
2014 // The two frames should be same-origin to each other, since the OAC header
2015 // value should be ignored.
2016 EXPECT_EQ("isolate me!",
2017 EvalJs(shell(), "window[0].document.body.textContent"));
2018}
2019
Alex Moshchuk837335f42021-12-03 22:56:312020// This test checks that same-process OriginAgentCluster won't crash and will
2021// apply properly when used on a localhost URL. See https://crbug.com/1276155.
2022IN_PROC_BROWSER_TEST_F(SameProcessOriginIsolationOptInHeaderTest, Localhost) {
2023 SetHeaderValue("?1");
2024 GURL url(https_server()->GetURL("localhost", "/isolate_origin"));
2025 url::Origin origin(url::Origin::Create(url));
2026
2027 EXPECT_TRUE(SiteIsolationPolicy::IsOriginAgentClusterEnabled());
2028 EXPECT_FALSE(
2029 SiteIsolationPolicy::IsProcessIsolationForOriginAgentClusterEnabled());
2030 EXPECT_FALSE(ShouldOriginGetOptInProcessIsolation(origin));
2031
2032 EXPECT_TRUE(NavigateToURL(shell(), url));
2033 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
2034 EXPECT_FALSE(root->current_frame_host()
2035 ->GetSiteInstance()
2036 ->RequiresDedicatedProcess());
2037 EXPECT_FALSE(ShouldOriginGetOptInProcessIsolation(origin));
2038 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
2039 auto isolation_result = policy->DetermineOriginAgentClusterIsolation(
2040 root->current_frame_host()->GetSiteInstance()->GetIsolationContext(),
2041 origin, MakeOACIsolationState(false));
2042 EXPECT_TRUE(isolation_result.is_origin_agent_cluster());
2043 EXPECT_FALSE(isolation_result.requires_origin_keyed_process());
W. James MacLeanc07dc41b2022-07-25 18:52:162044 EXPECT_TRUE(policy->HasOriginEverRequestedOriginAgentClusterValue(
Alex Moshchuk837335f42021-12-03 22:56:312045 web_contents()->GetBrowserContext(), origin));
2046}
2047
W. James MacLean4f51ce22021-03-22 22:19:042048// This test verifies that --disable-web-security overrides same-process
2049// OriginAgentCluster (i.e. disables it).
2050IN_PROC_BROWSER_TEST_F(SameProcessNoWebSecurityOriginIsolationOptInHeaderTest,
2051 DisableWebSecurityDisablesOriginAgentCluster) {
2052 // Make sure we request the header for OriginAgentCluster for the child; the
2053 // fact that this test uses --disable-web-security will override the header.
2054 SetHeaderValue("?1");
2055 GURL main_url(https_server()->GetURL("foo.com",
2056 "/cross_site_iframe_factory.html?"
2057 "foo.com(foo.com)"));
2058 GURL isolated_suborigin_url(
2059 https_server()->GetURL("isolated.foo.com", "/isolate_origin"));
2060 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Dave Tapuska4eea4112021-09-16 15:49:212061 EXPECT_EQ(2u, CollectAllRenderFrameHosts(shell()->web_contents()).size());
W. James MacLean4f51ce22021-03-22 22:19:042062
Carlos Caballero15caeeb2021-10-27 09:57:552063 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
W. James MacLean4f51ce22021-03-22 22:19:042064 FrameTreeNode* child_frame_node = root->child_at(0);
2065 EXPECT_TRUE(
2066 NavigateToURLFromRenderer(child_frame_node, isolated_suborigin_url));
2067
Mike West14d9fc32021-05-11 18:38:092068 // Web security is disabled so everything should be same-origin and
2069 // accessible across browsing contexts.
2070 EXPECT_EQ(false, EvalJs(child_frame_node, "window.originAgentCluster"));
Mike West1f8b4b22021-05-08 09:19:332071
W. James MacLean4f51ce22021-03-22 22:19:042072 std::string parent_body_content =
2073 EvalJs(root, "document.body.textContent").ExtractString();
2074 // Make sure that the child frame doesn't think it's isolated.
2075 EXPECT_EQ(parent_body_content,
2076 EvalJs(child_frame_node, "window.parent.document.body.textContent")
2077 .ExtractString());
2078}
2079
Domenic Denicola7dbd3d12021-01-08 21:26:562080// In this test the sub-origin isn't isolated because no header is set. It will
2081// have the same site instance as the main frame.
2082IN_PROC_BROWSER_TEST_F(OriginIsolationOptInHeaderTest,
W. James MacLean64ddbcc2020-01-24 22:34:222083 SimpleSubOriginNonIsolationTest) {
W. James MacLeana485f0e2021-01-29 17:18:052084 base::HistogramTester histograms;
W. James MacLean64ddbcc2020-01-24 22:34:222085 // Start off with an a(a) page, then navigate the subframe to an isolated sub
2086 // origin.
2087 GURL test_url(https_server()->GetURL("foo.com",
2088 "/cross_site_iframe_factory.html?"
2089 "foo.com(foo.com)"));
W. James MacLean1c40862c2020-04-27 21:05:572090 GURL isolated_suborigin_url(
2091 https_server()->GetURL("isolated.foo.com", "/isolate_origin"));
W. James MacLean64ddbcc2020-01-24 22:34:222092 EXPECT_TRUE(NavigateToURL(shell(), test_url));
Dave Tapuska4eea4112021-09-16 15:49:212093 EXPECT_EQ(2u, CollectAllRenderFrameHosts(shell()->web_contents()).size());
W. James MacLean64ddbcc2020-01-24 22:34:222094
Carlos Caballero15caeeb2021-10-27 09:57:552095 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
W. James MacLean64ddbcc2020-01-24 22:34:222096 FrameTreeNode* child_frame_node = root->child_at(0);
Lukasz Anforowicz69c25dfd2020-11-12 21:50:202097 EXPECT_TRUE(
2098 NavigateToURLFromRenderer(child_frame_node, isolated_suborigin_url));
W. James MacLean64ddbcc2020-01-24 22:34:222099 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
2100 child_frame_node->current_frame_host()->GetSiteInstance());
W. James MacLeana485f0e2021-01-29 17:18:052101 EXPECT_THAT(
2102 histograms.GetAllSamples("Navigation.OriginAgentCluster.Result"),
2103 testing::ElementsAre(base::Bucket(
2104 static_cast<int>(NavigationRequest::OriginAgentClusterEndResult::
2105 kNotRequestedAndNotOriginKeyed),
2106 3)));
W. James MacLean64ddbcc2020-01-24 22:34:222107}
2108
2109// This test verifies that renderer-initiated navigations to/from isolated
2110// sub-origins works as expected.
Domenic Denicola7dbd3d12021-01-08 21:26:562111IN_PROC_BROWSER_TEST_F(OriginIsolationOptInHeaderTest,
Domenic Denicola14103152020-04-03 19:40:452112 RendererInitiatedNavigations) {
Domenic Denicola7dbd3d12021-01-08 21:26:562113 SetHeaderValue("?1");
W. James MacLean64ddbcc2020-01-24 22:34:222114 GURL test_url(https_server()->GetURL("foo.com",
2115 "/cross_site_iframe_factory.html?"
2116 "foo.com(foo.com)"));
2117 EXPECT_TRUE(NavigateToURL(shell(), test_url));
Dave Tapuska4eea4112021-09-16 15:49:212118 EXPECT_EQ(2u, CollectAllRenderFrameHosts(shell()->web_contents()).size());
W. James MacLean64ddbcc2020-01-24 22:34:222119
Carlos Caballero15caeeb2021-10-27 09:57:552120 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
W. James MacLean64ddbcc2020-01-24 22:34:222121 FrameTreeNode* child = root->child_at(0);
2122
2123 GURL isolated_sub_origin_url(
W. James MacLean1c40862c2020-04-27 21:05:572124 https_server()->GetURL("isolated.foo.com", "/isolate_origin"));
W. James MacLean64ddbcc2020-01-24 22:34:222125 {
2126 // Navigate the child to an isolated origin.
2127 TestFrameNavigationObserver observer(child);
Avi Drissmanc91bd8e2021-04-19 23:58:442128 EXPECT_TRUE(ExecJs(
W. James MacLean64ddbcc2020-01-24 22:34:222129 child, "location.href = '" + isolated_sub_origin_url.spec() + "';"));
2130 observer.Wait();
2131 }
2132 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
2133 child->current_frame_host()->GetSiteInstance());
2134
2135 GURL non_isolated_sub_origin_url(
2136 https_server()->GetURL("bar.foo.com", "/title1.html"));
2137 {
2138 // Navigate the child to a non-isolated origin.
2139 TestFrameNavigationObserver observer(child);
Avi Drissmanc91bd8e2021-04-19 23:58:442140 EXPECT_TRUE(ExecJs(child, "location.href = '" +
2141 non_isolated_sub_origin_url.spec() + "';"));
W. James MacLean64ddbcc2020-01-24 22:34:222142 observer.Wait();
2143 }
2144 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
2145 child->current_frame_host()->GetSiteInstance());
2146}
2147
2148// Check that navigating a main frame from an non-isolated origin to an
2149// isolated origin and vice versa swaps processes and uses a new SiteInstance,
2150// both for renderer-initiated and browser-initiated navigations.
2151// Note: this test is essentially identical to
2152// IsolatedOriginTest.MainFrameNavigation.
Domenic Denicola7dbd3d12021-01-08 21:26:562153IN_PROC_BROWSER_TEST_F(OriginIsolationOptInHeaderTest, MainFrameNavigation) {
2154 SetHeaderValue("?1");
W. James MacLean64ddbcc2020-01-24 22:34:222155 GURL unisolated_url(https_server()->GetURL("www.foo.com", "/title1.html"));
W. James MacLean1c40862c2020-04-27 21:05:572156 GURL isolated_url(
2157 https_server()->GetURL("isolated.foo.com", "/isolate_origin"));
W. James MacLean64ddbcc2020-01-24 22:34:222158
2159 EXPECT_TRUE(NavigateToURL(shell(), unisolated_url));
2160
2161 // Open a same-site popup to keep the www.foo.com process alive.
2162 Shell* popup = OpenPopup(shell(), GURL(url::kAboutBlankURL), "foo");
2163 SiteInstance* unisolated_instance =
Dave Tapuska327c06c92022-06-13 20:31:512164 popup->web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
W. James MacLean64ddbcc2020-01-24 22:34:222165 RenderProcessHost* unisolated_process =
Dave Tapuska327c06c92022-06-13 20:31:512166 popup->web_contents()->GetPrimaryMainFrame()->GetProcess();
W. James MacLean64ddbcc2020-01-24 22:34:222167
2168 // Go to isolated.foo.com with a renderer-initiated navigation.
2169 EXPECT_TRUE(NavigateToURLFromRenderer(web_contents(), isolated_url));
2170 scoped_refptr<SiteInstance> isolated_instance =
2171 web_contents()->GetSiteInstance();
W. James MacLean222a2472020-08-14 22:00:222172 RenderProcessHost* isolated_process =
Dave Tapuska327c06c92022-06-13 20:31:512173 web_contents()->GetPrimaryMainFrame()->GetProcess();
W. James MacLean222a2472020-08-14 22:00:222174
2175 EXPECT_NE(unisolated_instance, isolated_instance);
2176 EXPECT_NE(unisolated_process, isolated_process);
W. James MacLean64ddbcc2020-01-24 22:34:222177
2178 // The site URL for isolated.foo.com should be the full origin rather than
2179 // scheme and eTLD+1.
2180 EXPECT_EQ(https_server()->GetURL("isolated.foo.com", "/"),
2181 isolated_instance->GetSiteURL());
2182
2183 // Now use a renderer-initiated navigation to go to an unisolated origin,
Ari Chivukula5350aad92021-08-10 02:42:242184 // www.foo.com. This should end up back in the `popup`'s process.
W. James MacLean64ddbcc2020-01-24 22:34:222185 EXPECT_TRUE(NavigateToURLFromRenderer(web_contents(), unisolated_url));
2186 EXPECT_EQ(unisolated_instance, web_contents()->GetSiteInstance());
Dave Tapuska327c06c92022-06-13 20:31:512187 EXPECT_EQ(unisolated_process,
2188 web_contents()->GetPrimaryMainFrame()->GetProcess());
W. James MacLean64ddbcc2020-01-24 22:34:222189
2190 // Now, perform a browser-initiated navigation to an isolated origin and
2191 // ensure that this ends up in a new process and SiteInstance for
2192 // isolated.foo.com.
2193 EXPECT_TRUE(NavigateToURL(shell(), isolated_url));
W. James MacLean222a2472020-08-14 22:00:222194 scoped_refptr<SiteInstance> isolated_instance2 =
2195 web_contents()->GetSiteInstance();
2196 RenderProcessHost* isolated_process2 =
Dave Tapuska327c06c92022-06-13 20:31:512197 web_contents()->GetPrimaryMainFrame()->GetProcess();
W. James MacLean222a2472020-08-14 22:00:222198 EXPECT_NE(unisolated_instance, isolated_instance2);
2199 EXPECT_NE(isolated_instance, isolated_instance2);
2200 EXPECT_NE(unisolated_process, isolated_process2);
W. James MacLean64ddbcc2020-01-24 22:34:222201
2202 // Go back to www.foo.com: this should end up in the unisolated process.
2203 {
2204 TestNavigationObserver back_observer(web_contents());
2205 web_contents()->GetController().GoBack();
2206 back_observer.Wait();
2207 }
2208
2209 EXPECT_EQ(unisolated_instance, web_contents()->GetSiteInstance());
Dave Tapuska327c06c92022-06-13 20:31:512210 EXPECT_EQ(unisolated_process,
2211 web_contents()->GetPrimaryMainFrame()->GetProcess());
W. James MacLean64ddbcc2020-01-24 22:34:222212
2213 // Go back again. This should go to isolated.foo.com in an isolated process.
2214 {
2215 TestNavigationObserver back_observer(web_contents());
2216 web_contents()->GetController().GoBack();
2217 back_observer.Wait();
2218 }
2219
2220 EXPECT_EQ(isolated_instance, web_contents()->GetSiteInstance());
Dave Tapuska327c06c92022-06-13 20:31:512221 EXPECT_NE(unisolated_process,
2222 web_contents()->GetPrimaryMainFrame()->GetProcess());
W. James MacLean64ddbcc2020-01-24 22:34:222223
2224 // Do a renderer-initiated navigation from isolated.foo.com to another
2225 // isolated origin and ensure there is a different isolated process.
2226 GURL second_isolated_url(
W. James MacLean1c40862c2020-04-27 21:05:572227 https_server()->GetURL("isolated.bar.com", "/isolate_origin"));
W. James MacLean64ddbcc2020-01-24 22:34:222228 EXPECT_TRUE(NavigateToURLFromRenderer(web_contents(), second_isolated_url));
2229 EXPECT_EQ(https_server()->GetURL("isolated.bar.com", "/"),
2230 web_contents()->GetSiteInstance()->GetSiteURL());
2231 EXPECT_NE(isolated_instance, web_contents()->GetSiteInstance());
2232 EXPECT_NE(unisolated_instance, web_contents()->GetSiteInstance());
2233}
2234
2235// This test ensures that if an origin starts off being isolated in a
2236// BrowsingInstance, it continues that way within the BrowsingInstance, even
2237// if a new policy is received that removes the opt-in request.
Domenic Denicola7dbd3d12021-01-08 21:26:562238IN_PROC_BROWSER_TEST_F(OriginIsolationOptInHeaderTest,
W. James MacLean64ddbcc2020-01-24 22:34:222239 OriginIsolationStateRetainedForBrowsingInstance) {
W. James MacLeana485f0e2021-01-29 17:18:052240 base::HistogramTester histograms;
Domenic Denicola7dbd3d12021-01-08 21:26:562241 SetHeaderValue("?1");
W. James MacLean64ddbcc2020-01-24 22:34:222242 // Start off with an a(a,a) page, then navigate the subframe to an isolated
2243 // sub origin.
2244 GURL test_url(https_server()->GetURL("foo.com",
2245 "/cross_site_iframe_factory.html?"
2246 "foo.com(foo.com, foo.com)"));
W. James MacLean1c40862c2020-04-27 21:05:572247 GURL isolated_suborigin_url(
2248 https_server()->GetURL("isolated.foo.com", "/isolate_origin"));
W. James MacLean64ddbcc2020-01-24 22:34:222249 EXPECT_TRUE(NavigateToURL(shell(), test_url));
Dave Tapuska4eea4112021-09-16 15:49:212250 EXPECT_EQ(3u, CollectAllRenderFrameHosts(shell()->web_contents()).size());
W. James MacLean64ddbcc2020-01-24 22:34:222251
Carlos Caballero15caeeb2021-10-27 09:57:552252 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
W. James MacLean64ddbcc2020-01-24 22:34:222253 FrameTreeNode* child_frame_node0 = root->child_at(0);
2254 FrameTreeNode* child_frame_node1 = root->child_at(1);
2255
Lukasz Anforowicz69c25dfd2020-11-12 21:50:202256 EXPECT_TRUE(
2257 NavigateToURLFromRenderer(child_frame_node0, isolated_suborigin_url));
W. James MacLean64ddbcc2020-01-24 22:34:222258 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
2259 child_frame_node0->current_frame_host()->GetSiteInstance());
2260
Domenic Denicola7dbd3d12021-01-08 21:26:562261 // Change the server's responses to stop isolating the sub-origin. It should
W. James MacLean64ddbcc2020-01-24 22:34:222262 // still be isolated, to remain consistent with the other frame.
Domenic Denicola7dbd3d12021-01-08 21:26:562263 SetHeaderValue("?0");
Domenic Denicolaf75040b2020-06-03 21:22:332264
2265 WebContentsConsoleObserver console_observer(shell()->web_contents());
2266 console_observer.SetPattern(
Domenic Denicola5fdc2b7f2021-01-15 20:29:172267 "The page did not request an origin-keyed agent cluster, but was put in "
2268 "one anyway*");
Domenic Denicolaf75040b2020-06-03 21:22:332269
Lukasz Anforowicz69c25dfd2020-11-12 21:50:202270 EXPECT_TRUE(
2271 NavigateToURLFromRenderer(child_frame_node1, isolated_suborigin_url));
Domenic Denicolaf75040b2020-06-03 21:22:332272
Fergal Daly7723f9d2022-10-29 07:03:132273 ASSERT_TRUE(console_observer.Wait());
Domenic Denicolaf75040b2020-06-03 21:22:332274
W. James MacLean64ddbcc2020-01-24 22:34:222275 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
2276 child_frame_node1->current_frame_host()->GetSiteInstance());
2277
2278 // The two sub-frames should be in the same site instance.
2279 EXPECT_EQ(child_frame_node0->current_frame_host()->GetSiteInstance(),
2280 child_frame_node1->current_frame_host()->GetSiteInstance());
2281
W. James MacLean1c40862c2020-04-27 21:05:572282 // Make sure the master opt-in list still has the origin tracked.
W. James MacLean64ddbcc2020-01-24 22:34:222283 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
W. James MacLeanc07dc41b2022-07-25 18:52:162284 EXPECT_TRUE(policy->HasOriginEverRequestedOriginAgentClusterValue(
W. James MacLeand7eb1562020-11-17 17:56:102285 web_contents()->GetBrowserContext(),
W. James MacLean1c40862c2020-04-27 21:05:572286 url::Origin::Create(isolated_suborigin_url)));
W. James MacLeana485f0e2021-01-29 17:18:052287
2288 EXPECT_THAT(
2289 histograms.GetAllSamples("Navigation.OriginAgentCluster.Result"),
2290 testing::ElementsAre(
2291 // Original loads of a(a,a) go here.
2292 base::Bucket(
2293 static_cast<int>(NavigationRequest::OriginAgentClusterEndResult::
2294 kNotRequestedAndNotOriginKeyed),
2295 3),
2296 // Second isolated subframe load goes here.
2297 base::Bucket(
2298 static_cast<int>(NavigationRequest::OriginAgentClusterEndResult::
2299 kNotRequestedButOriginKeyed),
2300 1),
2301 // First isolated subframe load goes here.
2302 base::Bucket(
2303 static_cast<int>(NavigationRequest::OriginAgentClusterEndResult::
2304 kRequestedAndOriginKeyed),
2305 1)));
W. James MacLean64ddbcc2020-01-24 22:34:222306}
2307
2308// This test ensures that if an origin starts off not being isolated in a
2309// BrowsingInstance, it continues that way within the BrowsingInstance, even
Domenic Denicola7dbd3d12021-01-08 21:26:562310// if the header starts being sent.
W. James MacLean1c40862c2020-04-27 21:05:572311// Case #1 where the non-opted-in origin is currently in the frame tree.
Domenic Denicola7dbd3d12021-01-08 21:26:562312IN_PROC_BROWSER_TEST_F(OriginIsolationOptInHeaderTest,
W. James MacLean1c40862c2020-04-27 21:05:572313 OriginNonIsolationStateRetainedForBrowsingInstance1) {
W. James MacLeana485f0e2021-01-29 17:18:052314 base::HistogramTester histograms;
Domenic Denicola7dbd3d12021-01-08 21:26:562315 SetHeaderValue("?0");
W. James MacLean64ddbcc2020-01-24 22:34:222316 // Start off with an a(a,a) page, then navigate the subframe to an isolated
2317 // sub origin.
2318 GURL test_url(https_server()->GetURL("foo.com",
2319 "/cross_site_iframe_factory.html?"
2320 "foo.com(foo.com, foo.com)"));
W. James MacLean1c40862c2020-04-27 21:05:572321 GURL isolated_suborigin_url(
2322 https_server()->GetURL("isolated.foo.com", "/isolate_origin"));
W. James MacLean64ddbcc2020-01-24 22:34:222323 EXPECT_TRUE(NavigateToURL(shell(), test_url));
Dave Tapuska4eea4112021-09-16 15:49:212324 EXPECT_EQ(3u, CollectAllRenderFrameHosts(shell()->web_contents()).size());
W. James MacLean64ddbcc2020-01-24 22:34:222325
Carlos Caballero15caeeb2021-10-27 09:57:552326 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
W. James MacLean64ddbcc2020-01-24 22:34:222327 FrameTreeNode* child_frame_node0 = root->child_at(0);
2328 FrameTreeNode* child_frame_node1 = root->child_at(1);
2329
Lukasz Anforowicz69c25dfd2020-11-12 21:50:202330 EXPECT_TRUE(
2331 NavigateToURLFromRenderer(child_frame_node0, isolated_suborigin_url));
W. James MacLean1c40862c2020-04-27 21:05:572332 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
2333 child_frame_node0->current_frame_host()->GetSiteInstance());
2334
Domenic Denicola7dbd3d12021-01-08 21:26:562335 // Change the server responses to start isolating the sub-origin. It should
W. James MacLean1c40862c2020-04-27 21:05:572336 // still be not-isolated, to remain consistent with the other frame.
Domenic Denicola7dbd3d12021-01-08 21:26:562337 SetHeaderValue("?1");
Domenic Denicolaf75040b2020-06-03 21:22:332338
2339 WebContentsConsoleObserver console_observer(shell()->web_contents());
2340 console_observer.SetPattern(
Domenic Denicola5fdc2b7f2021-01-15 20:29:172341 "The page requested an origin-keyed agent cluster using the "
2342 "Origin-Agent-Cluster header, but could not be origin-keyed*");
Domenic Denicolaf75040b2020-06-03 21:22:332343
Lukasz Anforowicz69c25dfd2020-11-12 21:50:202344 EXPECT_TRUE(
2345 NavigateToURLFromRenderer(child_frame_node1, isolated_suborigin_url));
Domenic Denicolaf75040b2020-06-03 21:22:332346
Fergal Daly7723f9d2022-10-29 07:03:132347 ASSERT_TRUE(console_observer.Wait());
Domenic Denicolaf75040b2020-06-03 21:22:332348
W. James MacLean1c40862c2020-04-27 21:05:572349 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
2350 child_frame_node1->current_frame_host()->GetSiteInstance());
2351
2352 // Make sure the master opt-in list has the origin listed.
2353 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
W. James MacLeanc07dc41b2022-07-25 18:52:162354 EXPECT_TRUE(policy->HasOriginEverRequestedOriginAgentClusterValue(
W. James MacLeand7eb1562020-11-17 17:56:102355 web_contents()->GetBrowserContext(),
W. James MacLean1c40862c2020-04-27 21:05:572356 url::Origin::Create(isolated_suborigin_url)));
W. James MacLeana485f0e2021-01-29 17:18:052357
2358 EXPECT_THAT(
2359 histograms.GetAllSamples("Navigation.OriginAgentCluster.Result"),
2360 testing::ElementsAre(
2361 // Original loads of a(a,a) go here.
2362 base::Bucket(
2363 static_cast<int>(NavigationRequest::OriginAgentClusterEndResult::
2364 kNotRequestedAndNotOriginKeyed),
2365 4),
2366 base::Bucket(
2367 static_cast<int>(NavigationRequest::OriginAgentClusterEndResult::
2368 kRequestedButNotOriginKeyed),
2369 1)));
W. James MacLean1c40862c2020-04-27 21:05:572370}
2371
2372// This test ensures that if an origin starts off not being isolated in a
2373// BrowsingInstance, it continues that way within the BrowsingInstance, even
Domenic Denicola7dbd3d12021-01-08 21:26:562374// if the header starts being sent.
W. James MacLean1c40862c2020-04-27 21:05:572375// Case #2 where the non-opted-in origin is currently not in the frame tree.
Domenic Denicola7dbd3d12021-01-08 21:26:562376IN_PROC_BROWSER_TEST_F(OriginIsolationOptInHeaderTest,
W. James MacLean1c40862c2020-04-27 21:05:572377 OriginNonIsolationStateRetainedForBrowsingInstance2) {
Domenic Denicola7dbd3d12021-01-08 21:26:562378 SetHeaderValue("?0");
W. James MacLean1c40862c2020-04-27 21:05:572379 // Start off with an a(a) page, then navigate the subframe to an isolated sub
2380 // origin.
2381 GURL test_url(https_server()->GetURL("foo.com",
2382 "/cross_site_iframe_factory.html?"
2383 "foo.com(foo.com)"));
2384 GURL isolated_suborigin_url(
2385 https_server()->GetURL("isolated.foo.com", "/isolate_origin"));
2386 EXPECT_TRUE(NavigateToURL(shell(), test_url));
Dave Tapuska4eea4112021-09-16 15:49:212387 EXPECT_EQ(2u, CollectAllRenderFrameHosts(shell()->web_contents()).size());
W. James MacLean1c40862c2020-04-27 21:05:572388
Carlos Caballero15caeeb2021-10-27 09:57:552389 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
W. James MacLean1c40862c2020-04-27 21:05:572390 FrameTreeNode* child_frame_node0 = root->child_at(0);
2391
2392 // Even though we're navigating to isolated.foo.com, there's no manifest
2393 // requesting opt-in, so it should end up in the same SiteInstance as the
2394 // main frame.
Lukasz Anforowicz69c25dfd2020-11-12 21:50:202395 EXPECT_TRUE(
2396 NavigateToURLFromRenderer(child_frame_node0, isolated_suborigin_url));
W. James MacLean1c40862c2020-04-27 21:05:572397 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
2398 child_frame_node0->current_frame_host()->GetSiteInstance());
2399
2400 // This navigation removes isolated_suborigin_url from the frame tree, but it
2401 // should still be in the session history.
Lukasz Anforowicz69c25dfd2020-11-12 21:50:202402 EXPECT_TRUE(NavigateToURLFromRenderer(
2403 child_frame_node0, https_server()->GetURL("foo.com", "/title1.html")));
W. James MacLean1c40862c2020-04-27 21:05:572404 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
2405 child_frame_node0->current_frame_host()->GetSiteInstance());
2406
Domenic Denicola7dbd3d12021-01-08 21:26:562407 // Change the server to start isolating the sub-origin. It should
W. James MacLean1c40862c2020-04-27 21:05:572408 // still be not isolated, to remain consistent with the other frame.
Domenic Denicola7dbd3d12021-01-08 21:26:562409 SetHeaderValue("?1");
Lukasz Anforowicz69c25dfd2020-11-12 21:50:202410 EXPECT_TRUE(
2411 NavigateToURLFromRenderer(child_frame_node0, isolated_suborigin_url));
W. James MacLean1c40862c2020-04-27 21:05:572412 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
2413 child_frame_node0->current_frame_host()->GetSiteInstance());
2414
2415 // Make sure the master opt-in list has the origin listed.
2416 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
W. James MacLeanc07dc41b2022-07-25 18:52:162417 EXPECT_TRUE(policy->HasOriginEverRequestedOriginAgentClusterValue(
W. James MacLeand7eb1562020-11-17 17:56:102418 web_contents()->GetBrowserContext(),
W. James MacLean1c40862c2020-04-27 21:05:572419 url::Origin::Create(isolated_suborigin_url)));
2420
2421 // Make sure the current browsing instance does *not* isolate the origin.
W. James MacLean7f76c2202021-11-15 16:27:492422 EXPECT_FALSE(policy
W. James MacLeand42fa812021-11-18 22:59:262423 ->DetermineOriginAgentClusterIsolation(
W. James MacLean7f76c2202021-11-15 16:27:492424 root->current_frame_host()
2425 ->GetSiteInstance()
2426 ->GetIsolationContext(),
2427 url::Origin::Create(isolated_suborigin_url),
2428 MakeOACIsolationState(false))
2429 .requires_origin_keyed_process());
W. James MacLean1c40862c2020-04-27 21:05:572430}
2431
2432// This test makes sure that a different tab in the same BrowsingInstance where
2433// an origin originally did not opt-in respects that state even if the
Domenic Denicola7dbd3d12021-01-08 21:26:562434// server sends a different header.
2435IN_PROC_BROWSER_TEST_F(OriginIsolationOptInHeaderTest,
W. James MacLean1c40862c2020-04-27 21:05:572436 OriginNonIsolationStateRetainedForPopup) {
Domenic Denicola7dbd3d12021-01-08 21:26:562437 SetHeaderValue("?0");
W. James MacLean1c40862c2020-04-27 21:05:572438 // Start off with an a(a,a) page, then navigate the subframe to an isolated
2439 // sub origin.
2440 GURL test_url(https_server()->GetURL("foo.com",
2441 "/cross_site_iframe_factory.html?"
2442 "foo.com(foo.com)"));
2443 GURL isolated_suborigin_url(
2444 https_server()->GetURL("isolated.foo.com", "/isolate_origin"));
2445 EXPECT_TRUE(NavigateToURL(shell(), test_url));
Dave Tapuska4eea4112021-09-16 15:49:212446 EXPECT_EQ(2u, CollectAllRenderFrameHosts(shell()->web_contents()).size());
W. James MacLean1c40862c2020-04-27 21:05:572447
Carlos Caballero15caeeb2021-10-27 09:57:552448 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
W. James MacLean1c40862c2020-04-27 21:05:572449 FrameTreeNode* child_frame_node0 = root->child_at(0);
2450
Lukasz Anforowicz69c25dfd2020-11-12 21:50:202451 EXPECT_TRUE(
2452 NavigateToURLFromRenderer(child_frame_node0, isolated_suborigin_url));
W. James MacLean64ddbcc2020-01-24 22:34:222453 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
2454 child_frame_node0->current_frame_host()->GetSiteInstance());
2455
Domenic Denicola7dbd3d12021-01-08 21:26:562456 // Change the server to start isolating the sub-origin. It should
2457 // not be isolated, to remain consistent with the other frame.
2458 SetHeaderValue("?1");
W. James MacLean64ddbcc2020-01-24 22:34:222459
W. James MacLean1c40862c2020-04-27 21:05:572460 // Open a popup in the same browsing instance, and navigate it to the
2461 // not-opted-in origin. Even though the manifest now requests isolation, it
2462 // should not opt-in since it's in the same BrowsingInstance where it
2463 // originally wasn't opted in.
2464 Shell* popup = OpenPopup(shell(), isolated_suborigin_url, "foo");
2465 auto* popup_web_contents = popup->web_contents();
2466 EXPECT_TRUE(
2467 NavigateToURLFromRenderer(popup_web_contents, isolated_suborigin_url));
2468
2469 EXPECT_EQ(shell()->web_contents()->GetSiteInstance()->GetBrowsingInstanceId(),
2470 popup_web_contents->GetSiteInstance()->GetBrowsingInstanceId());
2471
2472 // Make sure the current browsing instance does *not* isolate the origin.
2473 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
W. James MacLean7f76c2202021-11-15 16:27:492474 EXPECT_FALSE(policy
W. James MacLeand42fa812021-11-18 22:59:262475 ->DetermineOriginAgentClusterIsolation(
W. James MacLean7f76c2202021-11-15 16:27:492476 root->current_frame_host()
2477 ->GetSiteInstance()
2478 ->GetIsolationContext(),
2479 url::Origin::Create(isolated_suborigin_url),
2480 MakeOACIsolationState(false))
2481 .requires_origin_keyed_process());
W. James MacLean64ddbcc2020-01-24 22:34:222482}
2483
W. James MacLean89307252020-11-11 00:16:442484// This test creates a no-opener popup that is origin-isolated, and has two
2485// same-sub-origin iframes, one of which requests isolation and one that
2486// doesn't. The non-isolated child commits first, so the second child shouldn't
2487// get isolation, but more importantly we shouldn't crash on a NOTREACHED() in
2488// RenderFrameHostManager that is verifying that the second child frame was
2489// put in a compatible renderer process.
2490// https://crbug.com/1099718
Domenic Denicola7dbd3d12021-01-08 21:26:562491IN_PROC_BROWSER_TEST_F(OriginIsolationOptInHeaderTest,
W. James MacLean89307252020-11-11 00:16:442492 NoKillForBrowsingInstanceDifferencesInProcess) {
Domenic Denicola7dbd3d12021-01-08 21:26:562493 SetHeaderValue("?1");
W. James MacLean89307252020-11-11 00:16:442494 GURL opener_url(https_server()->GetURL("foo.com", "/title1.html"));
2495 EXPECT_TRUE(NavigateToURL(shell(), opener_url));
2496
2497 // Create content for popup. The first subframe is in a sub-domain of the
2498 // popup mainframe, which is an isolated base-origin. The second subframe is
2499 // in the same sub-origin as the first, but requests isolation. The isolation
2500 // request will fail, and both subframes will end up in the same site-locked
2501 // process as the opener document (due to subframe process reuse).
2502 GURL popup_subframe1_url(
2503 https_server()->GetURL("sub.foo.com", "/title1.html"));
2504 GURL popup_subframe2_url(
2505 https_server()->GetURL("sub.foo.com", "/isolate_origin"));
2506 // This is the HTML content for the popup mainframe.
2507 std::string popup_content = base::StringPrintf(
2508 R"(<!DOCTYPE html>
2509 <html><head>
2510 <meta charset="utf-8">
2511 <title>This page should not crash when window.open()ed</title>
2512 </head><body>
2513 <iframe src="%s"></iframe>
2514 <iframe></iframe>
2515 </body></html>)",
2516 popup_subframe1_url.spec().c_str());
2517 // The next navigation with relative URL = "/isolate_origin" should serve this
2518 // content.
2519 AddContentToQueue(popup_content);
2520
2521 // Open popup.
2522 GURL isolated_popup_url(https_server()->GetURL("foo.com", "/isolate_origin"));
2523 // Opening the popup with "noopener" guarantees that the isolated popup is in
2524 // a different BrowsingInstance from the opener.
2525 Shell* popup =
2526 OpenPopup(shell(), isolated_popup_url, "windowName1", "noopener",
2527 false /* expect_return_from_window_open */);
2528
2529 // If we got here without crashing, all that remains is to verify everything
2530 // is isolated/not-isolated as expected.
2531 ASSERT_NE(nullptr, popup);
2532 RenderFrameHostImpl* popup_root =
Dave Tapuska327c06c92022-06-13 20:31:512533 static_cast<WebContentsImpl*>(popup->web_contents())
2534 ->GetPrimaryMainFrame();
W. James MacLean89307252020-11-11 00:16:442535 EXPECT_EQ(2U, popup_root->child_count());
2536 FrameTreeNode* popup_child1 = popup_root->child_at(0);
2537 FrameTreeNode* popup_child2 = popup_root->child_at(1);
2538
2539 // Navigate the second child iframe after the first one has loaded.
2540 EXPECT_TRUE(NavigateFrameToURL(popup_child2, popup_subframe2_url));
2541
Ari Chivukula5350aad92021-08-10 02:42:242542 // Set cookie on `popup_child1` to make sure we don't get a renderer kill in
W. James MacLean89307252020-11-11 00:16:442543 // the process with the opener.
Avi Drissmanc91bd8e2021-04-19 23:58:442544 EXPECT_TRUE(ExecJs(popup_child1, "document.cookie = 'foo=bar';"));
W. James MacLean89307252020-11-11 00:16:442545 EXPECT_EQ("foo=bar", EvalJs(popup_child1, "document.cookie"));
2546
2547 // Verify state of various SiteIstances, BrowsingInstances and processes.
2548 SiteInstanceImpl* root_instance = popup_root->GetSiteInstance();
W. James MacLean7f76c2202021-11-15 16:27:492549 EXPECT_TRUE(root_instance->GetSiteInfo().requires_origin_keyed_process());
W. James MacLean89307252020-11-11 00:16:442550 SiteInstanceImpl* child1_instance =
2551 popup_child1->current_frame_host()->GetSiteInstance();
2552 SiteInstanceImpl* child2_instance =
2553 popup_child2->current_frame_host()->GetSiteInstance();
2554 EXPECT_EQ(child1_instance, child2_instance);
2555 EXPECT_NE(child1_instance, root_instance);
2556
2557 // Make sure child1 and the opener share the same process, but different
2558 // BrowsingInstances.
2559 SiteInstanceImpl* opener_instance =
2560 static_cast<WebContentsImpl*>(shell()->web_contents())->GetSiteInstance();
2561 EXPECT_NE(child1_instance->GetBrowsingInstanceId(),
2562 opener_instance->GetBrowsingInstanceId());
2563 EXPECT_EQ(child1_instance->GetProcess(), opener_instance->GetProcess());
W. James MacLean7f76c2202021-11-15 16:27:492564 EXPECT_FALSE(child2_instance->GetSiteInfo().requires_origin_keyed_process());
W. James MacLean89307252020-11-11 00:16:442565}
2566
2567// Same as NoKillForBrowsingInstanceDifferencesInProcess, except the starting
2568// page has an isolated iframe that matches the origin that won't get isolation
2569// in the popup's BrowsingInstance. Since this means that the first
2570// BrowsingInstance will show sub.foo.com as isolated, then if
2571// CanAccessDataForOrigin only checks the first BrowsingInstance it will get the
2572// wrong result.
Domenic Denicola7dbd3d12021-01-08 21:26:562573IN_PROC_BROWSER_TEST_F(OriginIsolationOptInHeaderTest,
W. James MacLean89307252020-11-11 00:16:442574 NoKillForBrowsingInstanceDifferencesInProcess2) {
Domenic Denicola7dbd3d12021-01-08 21:26:562575 SetHeaderValue("?1");
W. James MacLean89307252020-11-11 00:16:442576 // Start on a page with same-site iframe.
2577 GURL opener_url(https_server()->GetURL("foo.com", "/page_with_iframe.html"));
2578 EXPECT_TRUE(NavigateToURL(shell(), opener_url));
Carlos Caballero15caeeb2021-10-27 09:57:552579 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
W. James MacLean89307252020-11-11 00:16:442580 FrameTreeNode* child = root->child_at(0);
2581 GURL isolated_opener_iframe_url(
2582 https_server()->GetURL("sub.foo.com", "/isolate_origin"));
2583 EXPECT_TRUE(NavigateFrameToURL(child, isolated_opener_iframe_url));
2584 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
2585 child->current_frame_host()->GetSiteInstance());
2586 EXPECT_TRUE(child->current_frame_host()
2587 ->GetSiteInstance()
2588 ->GetSiteInfo()
W. James MacLean7f76c2202021-11-15 16:27:492589 .requires_origin_keyed_process());
W. James MacLean89307252020-11-11 00:16:442590
2591 // Create content for popup. The first subframe is in a sub-domain of the
2592 // popup mainframe, which is an isolated base-origin. The second subframe is
2593 // in the same sub-origin as the first, but requests isolation. The isolation
2594 // request will fail, and both subframes will end up in the same site-locked
2595 // process as the opener document (due to subframe process reuse).
2596 GURL popup_subframe1_url(
2597 https_server()->GetURL("sub.foo.com", "/title1.html"));
2598 GURL popup_subframe2_url(
2599 https_server()->GetURL("sub.foo.com", "/isolate_origin"));
2600 // This is the HTML content for the popup mainframe.
2601 std::string popup_content = base::StringPrintf(
2602 R"(<!DOCTYPE html>
2603 <html><head>
2604 <meta charset="utf-8">
2605 <title>This page should not crash when window.open()ed</title>
2606 </head><body>
2607 <iframe src="%s"></iframe>
2608 <iframe></iframe>
2609 </body></html>)",
2610 popup_subframe1_url.spec().c_str());
2611 // The next navigation with relative URL = "/isolate_origin" should serve this
2612 // content.
2613 AddContentToQueue(popup_content);
2614
2615 // Open popup.
2616 GURL isolated_popup_url(https_server()->GetURL("foo.com", "/isolate_origin"));
2617 // Opening the popup with "noopener" guarantees that the isolated popup is in
2618 // a different BrowsingInstance from the opener.
2619 Shell* popup =
2620 OpenPopup(shell(), isolated_popup_url, "windowName1", "noopener",
2621 false /* expect_return_from_window_open */);
2622
2623 // If we got here without crashing, all that remains is to verify everything
2624 // is isolated/not-isolated as expected.
2625 ASSERT_NE(nullptr, popup);
2626 RenderFrameHostImpl* popup_root =
Dave Tapuska327c06c92022-06-13 20:31:512627 static_cast<WebContentsImpl*>(popup->web_contents())
2628 ->GetPrimaryMainFrame();
W. James MacLean89307252020-11-11 00:16:442629 EXPECT_EQ(2U, popup_root->child_count());
2630 FrameTreeNode* popup_child1 = popup_root->child_at(0);
2631 FrameTreeNode* popup_child2 = popup_root->child_at(1);
2632
2633 // Navigate the second child iframe after the first one has loaded.
2634 EXPECT_TRUE(NavigateFrameToURL(popup_child2, popup_subframe2_url));
2635
Ari Chivukula5350aad92021-08-10 02:42:242636 // Set cookie on `popup_child1` to make sure we don't get a renderer kill in
W. James MacLean89307252020-11-11 00:16:442637 // the process with the opener.
Avi Drissmanc91bd8e2021-04-19 23:58:442638 EXPECT_TRUE(ExecJs(popup_child1, "document.cookie = 'foo=bar';"));
W. James MacLean89307252020-11-11 00:16:442639 EXPECT_EQ("foo=bar", EvalJs(popup_child1, "document.cookie"));
2640
2641 // Verify state of various SiteIstances, BrowsingInstances and processes.
2642 SiteInstanceImpl* root_instance = popup_root->GetSiteInstance();
W. James MacLean7f76c2202021-11-15 16:27:492643 EXPECT_TRUE(root_instance->GetSiteInfo().requires_origin_keyed_process());
W. James MacLean89307252020-11-11 00:16:442644 SiteInstanceImpl* child1_instance =
2645 popup_child1->current_frame_host()->GetSiteInstance();
2646 SiteInstanceImpl* child2_instance =
2647 popup_child2->current_frame_host()->GetSiteInstance();
2648 EXPECT_EQ(child1_instance, child2_instance);
2649 EXPECT_NE(child1_instance, root_instance);
2650
2651 // Make sure child1 and the opener share the same process, but different
2652 // BrowsingInstances.
2653 SiteInstanceImpl* opener_instance =
2654 static_cast<WebContentsImpl*>(shell()->web_contents())->GetSiteInstance();
2655 EXPECT_NE(child1_instance->GetBrowsingInstanceId(),
2656 opener_instance->GetBrowsingInstanceId());
2657 EXPECT_EQ(child1_instance->GetProcess(), opener_instance->GetProcess());
W. James MacLean7f76c2202021-11-15 16:27:492658 EXPECT_FALSE(child2_instance->GetSiteInfo().requires_origin_keyed_process());
W. James MacLean89307252020-11-11 00:16:442659}
2660
W. James MacLean64ddbcc2020-01-24 22:34:222661// This test handles the case where the base origin is isolated, but a
W. James MacLean222a2472020-08-14 22:00:222662// sub-origin isn't. In this case we need to place the sub-origin in a site-
2663// keyed SiteInstance with the same site URL as the origin-keyed SiteInstance
2664// used for the isolated base origin. Note: only the isolated base origin will
2665// have a port in this test, as the non-isolated sub-origin will have its port
2666// value stripped. The test IsolatedBaseOriginNoPorts tests the case where
2667// neither the isolated base origin nor the non-isolated sub-origin has a port
2668// value.
Domenic Denicola7dbd3d12021-01-08 21:26:562669IN_PROC_BROWSER_TEST_F(OriginIsolationOptInHeaderTest, IsolatedBaseOrigin) {
W. James MacLeana485f0e2021-01-29 17:18:052670 base::HistogramTester histograms;
Domenic Denicola7dbd3d12021-01-08 21:26:562671 SetHeaderValue("?1");
W. James MacLean64ddbcc2020-01-24 22:34:222672 // Start off with an isolated base-origin in an a(a) configuration, then
2673 // navigate the subframe to a sub-origin no requesting isolation.
2674 GURL test_url(https_server()->GetURL(
2675 "foo.com", "/isolated_base_origin_with_subframe.html"));
W. James MacLean222a2472020-08-14 22:00:222676 GURL non_isolated_sub_origin1(
2677 https_server()->GetURL("non_isolated1.foo.com", "/title1.html"));
2678 GURL non_isolated_sub_origin2(
2679 https_server()->GetURL("non_isolated2.foo.com", "/title1.html"));
W. James MacLean64ddbcc2020-01-24 22:34:222680 EXPECT_TRUE(NavigateToURL(shell(), test_url));
Dave Tapuska4eea4112021-09-16 15:49:212681 EXPECT_EQ(3u, CollectAllRenderFrameHosts(shell()->web_contents()).size());
W. James MacLean64ddbcc2020-01-24 22:34:222682
Carlos Caballero15caeeb2021-10-27 09:57:552683 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
W. James MacLean222a2472020-08-14 22:00:222684 FrameTreeNode* child_frame_node1 = root->child_at(0);
2685 FrameTreeNode* child_frame_node2 = root->child_at(1);
Lukasz Anforowicz69c25dfd2020-11-12 21:50:202686 EXPECT_TRUE(
2687 NavigateToURLFromRenderer(child_frame_node1, non_isolated_sub_origin1));
2688 EXPECT_TRUE(
2689 NavigateToURLFromRenderer(child_frame_node2, non_isolated_sub_origin2));
W. James MacLean222a2472020-08-14 22:00:222690
W. James MacLean1c40862c2020-04-27 21:05:572691 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
W. James MacLeand42fa812021-11-18 22:59:262692 EXPECT_TRUE(
2693 policy
2694 ->DetermineOriginAgentClusterIsolation(root->current_frame_host()
2695 ->GetSiteInstance()
2696 ->GetIsolationContext(),
2697 url::Origin::Create(test_url),
2698 MakeOACIsolationState(false))
2699 .requires_origin_keyed_process());
W. James MacLean7f76c2202021-11-15 16:27:492700 EXPECT_FALSE(policy
W. James MacLeand42fa812021-11-18 22:59:262701 ->DetermineOriginAgentClusterIsolation(
W. James MacLean7f76c2202021-11-15 16:27:492702 child_frame_node1->current_frame_host()
2703 ->GetSiteInstance()
2704 ->GetIsolationContext(),
2705 url::Origin::Create(non_isolated_sub_origin1),
2706 MakeOACIsolationState(false))
2707 .requires_origin_keyed_process());
2708 EXPECT_FALSE(policy
W. James MacLeand42fa812021-11-18 22:59:262709 ->DetermineOriginAgentClusterIsolation(
W. James MacLean7f76c2202021-11-15 16:27:492710 child_frame_node2->current_frame_host()
2711 ->GetSiteInstance()
2712 ->GetIsolationContext(),
2713 url::Origin::Create(non_isolated_sub_origin2),
2714 MakeOACIsolationState(false))
2715 .requires_origin_keyed_process());
W. James MacLean222a2472020-08-14 22:00:222716
2717 // Base origin and subdomains should have different SiteInstances.
W. James MacLean64ddbcc2020-01-24 22:34:222718 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
W. James MacLean222a2472020-08-14 22:00:222719 child_frame_node1->current_frame_host()->GetSiteInstance());
2720 EXPECT_TRUE(root->current_frame_host()
2721 ->GetSiteInstance()
2722 ->GetSiteInfo()
W. James MacLean7f76c2202021-11-15 16:27:492723 .requires_origin_keyed_process());
W. James MacLean222a2472020-08-14 22:00:222724 EXPECT_FALSE(child_frame_node1->current_frame_host()
2725 ->GetSiteInstance()
2726 ->GetSiteInfo()
W. James MacLean7f76c2202021-11-15 16:27:492727 .requires_origin_keyed_process());
W. James MacLean222a2472020-08-14 22:00:222728
2729 // Both non-isolated subdomains are in the same SiteInstance.
2730 EXPECT_EQ(child_frame_node1->current_frame_host()->GetSiteInstance(),
2731 child_frame_node2->current_frame_host()->GetSiteInstance());
2732 EXPECT_EQ(
2733 GURL("https://foo.com"),
2734 child_frame_node1->current_frame_host()->GetSiteInstance()->GetSiteURL());
2735
2736 // The base-origin and the children are in different processes.
2737 EXPECT_NE(
2738 root->current_frame_host()->GetSiteInstance()->GetProcess(),
2739 child_frame_node1->current_frame_host()->GetSiteInstance()->GetProcess());
2740
2741 // Make sure the master opt-in list has the base origin as isolated, but not
2742 // the sub-origins.
W. James MacLeand7eb1562020-11-17 17:56:102743 BrowserContext* browser_context = web_contents()->GetBrowserContext();
W. James MacLeanc07dc41b2022-07-25 18:52:162744 EXPECT_TRUE(policy->HasOriginEverRequestedOriginAgentClusterValue(
W. James MacLeand7eb1562020-11-17 17:56:102745 browser_context, url::Origin::Create(test_url)));
W. James MacLeanc07dc41b2022-07-25 18:52:162746 EXPECT_FALSE(policy->HasOriginEverRequestedOriginAgentClusterValue(
W. James MacLeand7eb1562020-11-17 17:56:102747 browser_context, url::Origin::Create(non_isolated_sub_origin1)));
W. James MacLeanc07dc41b2022-07-25 18:52:162748 EXPECT_FALSE(policy->HasOriginEverRequestedOriginAgentClusterValue(
W. James MacLeand7eb1562020-11-17 17:56:102749 browser_context, url::Origin::Create(non_isolated_sub_origin2)));
W. James MacLeana485f0e2021-01-29 17:18:052750
2751 EXPECT_THAT(
2752 histograms.GetAllSamples("Navigation.OriginAgentCluster.Result"),
2753 testing::ElementsAre(
2754 base::Bucket(
2755 static_cast<int>(NavigationRequest::OriginAgentClusterEndResult::
2756 kNotRequestedAndNotOriginKeyed),
2757 2),
2758 base::Bucket(
2759 static_cast<int>(NavigationRequest::OriginAgentClusterEndResult::
2760 kRequestedAndOriginKeyed),
2761 1)));
W. James MacLean222a2472020-08-14 22:00:222762}
2763
Domenic Denicola7dbd3d12021-01-08 21:26:562764// This test is the same as OriginIsolationOptInHeaderTest
W. James MacLean222a2472020-08-14 22:00:222765// .IsolatedBaseOrigin except it uses port-free URLs. This is critical since we
2766// can have two SiteInstances with the same SiteURL as long as one is
2767// origin-keyed and the other isn't. Site URLs used to be used as map-keys but
2768// with opt-in origin isolation we need to also consider the keying flag.
2769// When the URLs all have non-default ports, we will never have duplicate
2770// site URLs since the site-keyed one will have the port stripped.
2771IN_PROC_BROWSER_TEST_F(OriginIsolationOptInHeaderTest,
2772 IsolatedBaseOriginNoPorts) {
2773 GURL isolated_base_origin_url("https://foo.com");
2774 GURL non_isolated_sub_origin_url_a("https://a.foo.com");
2775 GURL non_isolated_sub_origin_url_b("https://b.foo.com");
2776
2777 // Since the embedded test server only works for URLs with non-default ports,
2778 // use a URLLoaderInterceptor to mimic port-free operation. This allows the
2779 // rest of the test to operate as if all URLs are using the default ports.
2780 URLLoaderInterceptor interceptor(base::BindLambdaForTesting(
2781 [&](URLLoaderInterceptor::RequestParams* params) {
2782 if (params->url_request.url.host() == "foo.com") {
Jagadesh P1c8699c22023-11-13 14:09:372783 if (params->url_request.url.path() != "/") {
W. James MacLean222a2472020-08-14 22:00:222784 return false;
Jagadesh P1c8699c22023-11-13 14:09:372785 }
W. James MacLean222a2472020-08-14 22:00:222786
2787 const std::string headers =
2788 "HTTP/1.1 200 OK\n"
2789 "Content-Type: text/html\n"
Domenic Denicola942aca82020-12-12 06:51:592790 "Origin-Agent-Cluster: ?1\n";
W. James MacLean222a2472020-08-14 22:00:222791 // Note: this call would normally get the headers from
2792 // isolated_base_origin_with_subframe.html.mock-http-headers,
2793 // but those are meant for use with a
Domenic Denicola7dbd3d12021-01-08 21:26:562794 // OriginIsolationOptInHeaderTest. and won't work here, so we
W. James MacLean222a2472020-08-14 22:00:222795 // override them.
2796 URLLoaderInterceptor::WriteResponse(
2797 "content/test/data/isolated_base_origin_with_subframe.html",
Arthur Sonzognic686e8f2024-01-11 08:36:372798 params->client.get(), &headers, std::optional<net::SSLInfo>());
W. James MacLean222a2472020-08-14 22:00:222799 return true;
2800 }
2801 if (params->url_request.url.host() == "a.foo.com" ||
2802 params->url_request.url.host() == "b.foo.com") {
2803 URLLoaderInterceptor::WriteResponse("content/test/data/title1.html",
2804 params->client.get());
2805 return true;
2806 }
2807 // Not handled by us.
2808 return false;
2809 }));
2810
2811 // Load the isolated base url.
2812 EXPECT_TRUE(NavigateToURL(shell(), isolated_base_origin_url));
Dave Tapuska4eea4112021-09-16 15:49:212813 EXPECT_EQ(3u, CollectAllRenderFrameHosts(shell()->web_contents()).size());
W. James MacLean222a2472020-08-14 22:00:222814
Carlos Caballero15caeeb2021-10-27 09:57:552815 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
W. James MacLean222a2472020-08-14 22:00:222816 FrameTreeNode* child_frame_node1 = root->child_at(0);
2817 FrameTreeNode* child_frame_node2 = root->child_at(1);
Lukasz Anforowicz69c25dfd2020-11-12 21:50:202818 EXPECT_TRUE(NavigateToURLFromRenderer(child_frame_node1,
2819 non_isolated_sub_origin_url_a));
2820 EXPECT_TRUE(NavigateToURLFromRenderer(child_frame_node2,
2821 non_isolated_sub_origin_url_b));
W. James MacLean222a2472020-08-14 22:00:222822
2823 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
W. James MacLean7f76c2202021-11-15 16:27:492824 EXPECT_TRUE(policy
W. James MacLeand42fa812021-11-18 22:59:262825 ->DetermineOriginAgentClusterIsolation(
W. James MacLean7f76c2202021-11-15 16:27:492826 root->current_frame_host()
2827 ->GetSiteInstance()
2828 ->GetIsolationContext(),
2829 url::Origin::Create(isolated_base_origin_url),
2830 MakeOACIsolationState(false))
2831 .requires_origin_keyed_process());
2832 EXPECT_FALSE(policy
W. James MacLeand42fa812021-11-18 22:59:262833 ->DetermineOriginAgentClusterIsolation(
W. James MacLean7f76c2202021-11-15 16:27:492834 child_frame_node1->current_frame_host()
2835 ->GetSiteInstance()
2836 ->GetIsolationContext(),
2837 url::Origin::Create(non_isolated_sub_origin_url_a),
2838 MakeOACIsolationState(false))
2839 .requires_origin_keyed_process());
2840 EXPECT_FALSE(policy
W. James MacLeand42fa812021-11-18 22:59:262841 ->DetermineOriginAgentClusterIsolation(
W. James MacLean7f76c2202021-11-15 16:27:492842 child_frame_node2->current_frame_host()
2843 ->GetSiteInstance()
2844 ->GetIsolationContext(),
2845 url::Origin::Create(non_isolated_sub_origin_url_b),
2846 MakeOACIsolationState(false))
2847 .requires_origin_keyed_process());
W. James MacLean222a2472020-08-14 22:00:222848 // Base origin and subdomains should have different SiteInstances.
2849 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
2850 child_frame_node1->current_frame_host()->GetSiteInstance());
2851 EXPECT_TRUE(root->current_frame_host()
2852 ->GetSiteInstance()
2853 ->GetSiteInfo()
W. James MacLean7f76c2202021-11-15 16:27:492854 .requires_origin_keyed_process());
W. James MacLean222a2472020-08-14 22:00:222855 EXPECT_FALSE(child_frame_node1->current_frame_host()
2856 ->GetSiteInstance()
2857 ->GetSiteInfo()
W. James MacLean7f76c2202021-11-15 16:27:492858 .requires_origin_keyed_process());
W. James MacLean222a2472020-08-14 22:00:222859
2860 // Both SiteInstances should have the same site URL, because they have no
2861 // port.
2862 EXPECT_EQ(
2863 root->current_frame_host()->GetSiteInstance()->GetSiteURL(),
2864 child_frame_node1->current_frame_host()->GetSiteInstance()->GetSiteURL());
2865 EXPECT_NE(root->current_frame_host()->GetSiteInstance()->GetSiteInfo(),
2866 child_frame_node1->current_frame_host()
2867 ->GetSiteInstance()
2868 ->GetSiteInfo());
2869
2870 // Both non-isolated subdomains are in the same SiteInstance.
2871 EXPECT_EQ(child_frame_node1->current_frame_host()->GetSiteInstance(),
2872 child_frame_node2->current_frame_host()->GetSiteInstance());
2873
2874 // The base-origin and the children are in different processes.
2875 EXPECT_NE(
2876 root->current_frame_host()->GetSiteInstance()->GetProcess(),
2877 child_frame_node1->current_frame_host()->GetSiteInstance()->GetProcess());
2878
2879 // Make sure the master opt-in list has the base origin isolated and the sub
2880 // origins both not isolated.
W. James MacLeand7eb1562020-11-17 17:56:102881 BrowserContext* browser_context = web_contents()->GetBrowserContext();
W. James MacLeanc07dc41b2022-07-25 18:52:162882 EXPECT_TRUE(policy->HasOriginEverRequestedOriginAgentClusterValue(
W. James MacLeand7eb1562020-11-17 17:56:102883 browser_context, url::Origin::Create(isolated_base_origin_url)));
W. James MacLeanc07dc41b2022-07-25 18:52:162884 EXPECT_FALSE(policy->HasOriginEverRequestedOriginAgentClusterValue(
W. James MacLeand7eb1562020-11-17 17:56:102885 browser_context, url::Origin::Create(non_isolated_sub_origin_url_a)));
W. James MacLeanc07dc41b2022-07-25 18:52:162886 EXPECT_FALSE(policy->HasOriginEverRequestedOriginAgentClusterValue(
W. James MacLeand7eb1562020-11-17 17:56:102887 browser_context, url::Origin::Create(non_isolated_sub_origin_url_b)));
W. James MacLean64ddbcc2020-01-24 22:34:222888}
2889
W. James MacLean17cc71f2020-11-12 21:20:122890IN_PROC_BROWSER_TEST_F(OriginIsolationOptInHeaderTest,
W. James MacLeand7eb1562020-11-17 17:56:102891 SeparateBrowserContextTest) {
W. James MacLean17cc71f2020-11-12 21:20:122892 GURL isolated_origin_url(
2893 https_server()->GetURL("isolated.foo.com", "/isolate_origin"));
W. James MacLeand7eb1562020-11-17 17:56:102894 Shell* shell_otr = CreateOffTheRecordBrowser();
W. James MacLean17cc71f2020-11-12 21:20:122895
2896 EXPECT_NE(shell()->web_contents()->GetBrowserContext(),
W. James MacLeand7eb1562020-11-17 17:56:102897 shell_otr->web_contents()->GetBrowserContext());
W. James MacLean17cc71f2020-11-12 21:20:122898
2899 // The isolation header is not present, so this navigation will result in a
2900 // site-keyed instance.
W. James MacLeand7eb1562020-11-17 17:56:102901 EXPECT_TRUE(NavigateToURL(shell_otr, isolated_origin_url));
2902 WebContentsImpl* web_contents_shell_otr =
2903 static_cast<WebContentsImpl*>(shell_otr->web_contents());
2904 SiteInstanceImpl* site_instance_shell_otr =
Carlos Caballero15caeeb2021-10-27 09:57:552905 web_contents_shell_otr->GetPrimaryFrameTree()
2906 .root()
W. James MacLeand7eb1562020-11-17 17:56:102907 ->current_frame_host()
2908 ->GetSiteInstance();
W. James MacLean7f76c2202021-11-15 16:27:492909 EXPECT_FALSE(
2910 site_instance_shell_otr->GetSiteInfo().requires_origin_keyed_process());
W. James MacLean17cc71f2020-11-12 21:20:122911
2912 url::Origin isolated_origin = url::Origin::Create(isolated_origin_url);
2913 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
2914
2915 // Now navigate a different BrowserContext to the same origin, but this time
2916 // requesting isolation. The presence of the site-keyed instance in a
2917 // different BrowsingInstance shouldn't prevent this navigation from being
W. James MacLeand7eb1562020-11-17 17:56:102918 // isolated. The presence of the site-keyed instance in a different
2919 // BrowsingInstance (whether in the same BrowserContext or a different one)
2920 // shouldn't prevent this navigation from being isolated. We'll test
2921 // cross-BrowserContext interactions below.
W. James MacLean17cc71f2020-11-12 21:20:122922 SetHeaderValue("?1");
2923 EXPECT_TRUE(NavigateToURL(shell(), isolated_origin_url));
W. James MacLean7f76c2202021-11-15 16:27:492924 EXPECT_TRUE(policy
W. James MacLeand42fa812021-11-18 22:59:262925 ->DetermineOriginAgentClusterIsolation(
W. James MacLean7f76c2202021-11-15 16:27:492926 static_cast<WebContentsImpl*>(shell()->web_contents())
2927 ->GetPrimaryFrameTree()
2928 .root()
2929 ->current_frame_host()
2930 ->GetSiteInstance()
2931 ->GetIsolationContext(),
2932 isolated_origin, MakeOACIsolationState(false))
2933 .requires_origin_keyed_process());
W. James MacLean17cc71f2020-11-12 21:20:122934
2935 // Make sure isolating the origin in the main context didn't affect it in the
2936 // off-the-record context. Specifically, if the opting-in in shell() did leak
Ari Chivukula5350aad92021-08-10 02:42:242937 // to shell_otr, then `isolated_origin` will be recorded as non-opted in in
W. James MacLeand7eb1562020-11-17 17:56:102938 // that BrowsingInstance. The following check makes sure that
Ari Chivukula5350aad92021-08-10 02:42:242939 // `isolated_origin` is not in the non-opt-in list, verifying that the
W. James MacLeand7eb1562020-11-17 17:56:102940 // internal bookkeeping is specific to each BrowserContext. Isolating the
2941 // bookkeeping by BrowserContext prevents timing attacks from detecting
2942 // whether an origin has been visited in another BrowserContext by detecting
2943 // the global walk.
Ari Chivukula5350aad92021-08-10 02:42:242944 // At this stage, `isolated_origin` is not in the non-opt-in list for this
W. James MacLeand7eb1562020-11-17 17:56:102945 // BrowsingInstance, since we haven't yet done a global walk in the OTR
W. James MacLeand42fa812021-11-18 22:59:262946 // BrowserContext, so DetermineOriginAgentClusterIsolation will return true.
W. James MacLeand7eb1562020-11-17 17:56:102947 // However, during the navigation by the OpenPopup call below that global walk
W. James MacLean46fc0b62020-11-18 16:18:452948 // will be triggered before the url's isolation status is set. This walk is
2949 // triggered by the call to CheckForIsolationOptIn() in
2950 // NavigationRequest::OnResponseStarted().
W. James MacLean7f76c2202021-11-15 16:27:492951 EXPECT_TRUE(policy
W. James MacLeand42fa812021-11-18 22:59:262952 ->DetermineOriginAgentClusterIsolation(
W. James MacLean7f76c2202021-11-15 16:27:492953 static_cast<WebContentsImpl*>(shell_otr->web_contents())
2954 ->GetPrimaryFrameTree()
2955 .root()
2956 ->current_frame_host()
2957 ->GetSiteInstance()
2958 ->GetIsolationContext(),
2959 isolated_origin, MakeOACIsolationState(true))
2960 .requires_origin_keyed_process());
W. James MacLeand7eb1562020-11-17 17:56:102961
2962 // Make sure the OTR context does a global (i.e. profile) walk if we attempt
2963 // to now opt-in when we didn't before.
2964 Shell* popup = OpenPopup(shell_otr, isolated_origin_url, "popup_otr");
2965 WebContentsImpl* web_contents_popup =
2966 static_cast<WebContentsImpl*>(popup->web_contents());
Carlos Caballero15caeeb2021-10-27 09:57:552967 SiteInstanceImpl* site_instance_popup =
2968 web_contents_popup->GetPrimaryFrameTree()
2969 .root()
2970 ->current_frame_host()
2971 ->GetSiteInstance();
W. James MacLeand7eb1562020-11-17 17:56:102972 // This shouldn't be isolated because we already have a non-isolated version
2973 // of this origin in shell_otr's main frame, in the same BrowsingInstance.
W. James MacLean7f76c2202021-11-15 16:27:492974 EXPECT_FALSE(
2975 site_instance_popup->GetSiteInfo().requires_origin_keyed_process());
Ari Chivukula5350aad92021-08-10 02:42:242976 // Since the OpenPopup navigation triggered a global walk, `isolated_origin`
W. James MacLeand7eb1562020-11-17 17:56:102977 // was added to the non-opt-in list, so now calling
W. James MacLeand42fa812021-11-18 22:59:262978 // DetermineOriginAgentClusterIsolation will return false.
W. James MacLean7f76c2202021-11-15 16:27:492979 EXPECT_FALSE(policy
W. James MacLeand42fa812021-11-18 22:59:262980 ->DetermineOriginAgentClusterIsolation(
W. James MacLean7f76c2202021-11-15 16:27:492981 site_instance_popup->GetIsolationContext(),
2982 isolated_origin, MakeOACIsolationState(true))
2983 .requires_origin_keyed_process());
W. James MacLeand7eb1562020-11-17 17:56:102984
2985 // Opening a new tab in the OTR profile, which will create a new
2986 // BrowsingInstance, should be allowed to isolate.
2987 Shell* shell_otr_tab2 = CreateOffTheRecordBrowser();
2988 EXPECT_TRUE(NavigateToURL(shell_otr_tab2, isolated_origin_url));
2989 WebContentsImpl* web_contenst_shell_otr_tab2 =
2990 static_cast<WebContentsImpl*>(shell_otr_tab2->web_contents());
2991 SiteInstanceImpl* site_instance_shell_otr_tab2 =
Carlos Caballero15caeeb2021-10-27 09:57:552992 web_contenst_shell_otr_tab2->GetPrimaryFrameTree()
2993 .root()
W. James MacLeand7eb1562020-11-17 17:56:102994 ->current_frame_host()
2995 ->GetSiteInstance();
W. James MacLean7f76c2202021-11-15 16:27:492996 EXPECT_TRUE(site_instance_shell_otr_tab2->GetSiteInfo()
2997 .requires_origin_keyed_process());
2998 EXPECT_TRUE(policy
W. James MacLeand42fa812021-11-18 22:59:262999 ->DetermineOriginAgentClusterIsolation(
W. James MacLean7f76c2202021-11-15 16:27:493000 site_instance_shell_otr_tab2->GetIsolationContext(),
3001 isolated_origin, MakeOACIsolationState(true))
3002 .requires_origin_keyed_process());
W. James MacLean17cc71f2020-11-12 21:20:123003}
3004
Rakina Zata Amniafd3c6582021-11-30 06:19:173005// This test creates a scenario where we have a frame that is on the initial
3006// NavigationEntry, and then we created another frame with the same origin
W. James MacLeanb70fab82020-05-01 18:51:143007// that opts-in to isolation. The opt-in triggers a walk of the session history
3008// and the frame tree ... the session history won't pick up the first frame, but
3009// the frame-tree walk should.
Alison Gale770f3fc2024-04-27 00:39:583010// TODO(crbug.com/40467594): Once every created frame is guaranteed to
Rakina Zata Amniafd3c6582021-11-30 06:19:173011// have a FrameNavigationEntry and thus represented in the sesion history, we
3012// probably can remove the frame-tree walk.
Domenic Denicola7dbd3d12021-01-08 21:26:563013IN_PROC_BROWSER_TEST_F(OriginIsolationOptInHeaderTest, FrameTreeTest) {
W. James MacLeanb70fab82020-05-01 18:51:143014 EXPECT_TRUE(NavigateToURL(shell(),
3015 https_server()->GetURL("bar.com", "/title1.html")));
3016 // Have tab1 call window.open() to create blank tab2.
Carlos Caballero15caeeb2021-10-27 09:57:553017 FrameTreeNode* tab1_root = web_contents()->GetPrimaryFrameTree().root();
W. James MacLeanb70fab82020-05-01 18:51:143018 ShellAddedObserver new_shell_observer;
Rakina Zata Amni3163cf42021-12-08 08:27:583019 ASSERT_TRUE(ExecJs(tab1_root->current_frame_host(),
3020 "window.w = window.open('/nocontent')"));
W. James MacLeanb70fab82020-05-01 18:51:143021 Shell* tab2_shell = new_shell_observer.GetShell();
3022
3023 // Create iframe in tab2.
3024 FrameTreeNode* tab2_root =
3025 static_cast<WebContentsImpl*>(tab2_shell->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:553026 ->GetPrimaryFrameTree()
3027 .root();
Avi Drissmanc91bd8e2021-04-19 23:58:443028 ASSERT_TRUE(ExecJs(tab2_root->current_frame_host(),
3029 "var iframe = document.createElement('iframe');"
3030 "document.body.appendChild(iframe);"));
W. James MacLeanb70fab82020-05-01 18:51:143031 EXPECT_EQ(1U, tab2_root->child_count());
3032 FrameTreeNode* tab2_child = tab2_root->child_at(0);
3033 GURL isolated_origin_url(
3034 https_server()->GetURL("isolated.foo.com", "/isolate_origin"));
Rakina Zata Amniafd3c6582021-11-30 06:19:173035 // Navigate the iframe in tab2 to `isolated_origin_url` without requesting
3036 // isolation, so it won't be isolated.
W. James MacLeanb70fab82020-05-01 18:51:143037 EXPECT_TRUE(NavigateFrameToURL(tab2_child, isolated_origin_url));
3038
3039 // Do a browser-initiated navigation of tab1 to the same origin, but isolate
Ari Chivukula5350aad92021-08-10 02:42:243040 // it this time. This should place the two frames with `isolated_origin_url`
W. James MacLeanb70fab82020-05-01 18:51:143041 // into different BrowsingInstances.
Domenic Denicola7dbd3d12021-01-08 21:26:563042 SetHeaderValue("?1");
W. James MacLeanb70fab82020-05-01 18:51:143043 EXPECT_TRUE(NavigateToURL(shell(), isolated_origin_url));
3044
3045 // Since the same origin exists in two tabs, but one is isolated and the other
3046 // isn't, we expect them to be in different BrowsingInstances.
W. James MacLeanb70fab82020-05-01 18:51:143047 EXPECT_NE(tab1_root->current_frame_host()->GetSiteInstance(),
3048 tab2_child->current_frame_host()->GetSiteInstance());
3049 EXPECT_NE(tab1_root->current_frame_host()
3050 ->GetSiteInstance()
3051 ->GetIsolationContext()
3052 .browsing_instance_id(),
3053 tab2_child->current_frame_host()
3054 ->GetSiteInstance()
3055 ->GetIsolationContext()
3056 .browsing_instance_id());
W. James MacLeanb70fab82020-05-01 18:51:143057
W. James MacLean46cf26212020-10-01 16:43:373058 url::Origin isolated_origin = url::Origin::Create(isolated_origin_url);
3059 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
Ari Chivukula5350aad92021-08-10 02:42:243060 // Verify that `isolated origin` is in the non-opt-in list for tab2's
W. James MacLean46cf26212020-10-01 16:43:373061 // child's BrowsingInstance. We do this by requesting opt-in for the origin,
3062 // then verifying that it is denied by DoesOriginRequestOptInIsolation.
W. James MacLean7f76c2202021-11-15 16:27:493063 EXPECT_FALSE(policy
W. James MacLeand42fa812021-11-18 22:59:263064 ->DetermineOriginAgentClusterIsolation(
W. James MacLean7f76c2202021-11-15 16:27:493065 tab2_child->current_frame_host()
3066 ->GetSiteInstance()
3067 ->GetIsolationContext(),
3068 isolated_origin, MakeOACIsolationState(true))
3069 .requires_origin_keyed_process());
Ari Chivukula5350aad92021-08-10 02:42:243070 // Verify that `isolated_origin` in tab1 is indeed isolated.
W. James MacLean7f76c2202021-11-15 16:27:493071 EXPECT_TRUE(policy
W. James MacLeand42fa812021-11-18 22:59:263072 ->DetermineOriginAgentClusterIsolation(
W. James MacLean7f76c2202021-11-15 16:27:493073 tab1_root->current_frame_host()
3074 ->GetSiteInstance()
3075 ->GetIsolationContext(),
3076 isolated_origin, MakeOACIsolationState(false))
3077 .requires_origin_keyed_process());
Rakina Zata Amnie2d31312022-11-18 03:38:453078 // Verify that the tab2 child frame is on the initial NavigationEntry.
3079 EXPECT_TRUE(tab2_shell->web_contents()
3080 ->GetController()
3081 .GetLastCommittedEntry()
3082 ->IsInitialEntry());
W. James MacLeanb70fab82020-05-01 18:51:143083
3084 // Now, create a second frame in tab2 and navigate it to
Ari Chivukula5350aad92021-08-10 02:42:243085 // `isolated_origin_url`. Even though isolation is requested, it should not
W. James MacLeanb70fab82020-05-01 18:51:143086 // be isolated.
Avi Drissmanc91bd8e2021-04-19 23:58:443087 ASSERT_TRUE(ExecJs(tab2_root->current_frame_host(),
3088 "var iframe = document.createElement('iframe');"
3089 "document.body.appendChild(iframe);"));
W. James MacLeanb70fab82020-05-01 18:51:143090 EXPECT_EQ(2U, tab2_root->child_count());
3091 FrameTreeNode* tab2_child2 = tab2_root->child_at(1);
3092 NavigateFrameToURL(tab2_child2, isolated_origin_url);
3093 EXPECT_EQ(tab2_child->current_frame_host()->GetSiteInstance(),
3094 tab2_child2->current_frame_host()->GetSiteInstance());
3095
3096 // Check that the two child frames can script each other.
Avi Drissmanc91bd8e2021-04-19 23:58:443097 EXPECT_TRUE(ExecJs(tab2_child2, R"(
W. James MacLeanb70fab82020-05-01 18:51:143098 parent.frames[0].cross_frame_property_test = 'hello from t2c2'; )"));
Avi Drissmanc91bd8e2021-04-19 23:58:443099 EXPECT_EQ("hello from t2c2",
3100 EvalJs(tab2_child, "window.cross_frame_property_test;"));
W. James MacLeanb70fab82020-05-01 18:51:143101}
3102
3103// Similar to FrameTreeTest, but we stop the navigation that's not requesting
3104// isolation at the pending commit state in tab2, then verify that the FrameTree
3105// walk has correctly registered the origin as non-isolated in tab2, but
3106// isolated in tab1.
Domenic Denicola7dbd3d12021-01-08 21:26:563107IN_PROC_BROWSER_TEST_F(OriginIsolationOptInHeaderTest,
W. James MacLeanb70fab82020-05-01 18:51:143108 FrameTreeTestPendingCommit) {
3109 GURL isolated_origin_url(
3110 https_server()->GetURL("isolated.foo.com", "/isolate_origin"));
3111 TestNavigationManager non_isolated_delayer(shell()->web_contents(),
3112 isolated_origin_url);
3113 shell()->web_contents()->GetController().LoadURL(
3114 isolated_origin_url, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
3115 EXPECT_TRUE(non_isolated_delayer.WaitForResponse());
3116
3117 Shell* tab2 = CreateBrowser();
3118 // Do a browser-initiated navigation of tab2 to the same origin, but isolate
Ari Chivukula5350aad92021-08-10 02:42:243119 // it this time. This should place the two frames with `isolated_origin_url`
W. James MacLeanb70fab82020-05-01 18:51:143120 // into different BrowsingInstances.
Domenic Denicola7dbd3d12021-01-08 21:26:563121 SetHeaderValue("?1");
W. James MacLeanb70fab82020-05-01 18:51:143122 EXPECT_TRUE(NavigateToURL(tab2, isolated_origin_url));
3123
3124 // Now commit the non-isolated navigation.
Fergal Daly83bc3cd2023-01-18 00:22:543125 ASSERT_TRUE(non_isolated_delayer.WaitForNavigationFinished());
W. James MacLeanb70fab82020-05-01 18:51:143126
Carlos Caballero15caeeb2021-10-27 09:57:553127 FrameTreeNode* tab1_root = web_contents()->GetPrimaryFrameTree().root();
W. James MacLeanb70fab82020-05-01 18:51:143128 SiteInstanceImpl* tab1_site_instance =
3129 tab1_root->current_frame_host()->GetSiteInstance();
3130 FrameTreeNode* tab2_root = static_cast<WebContentsImpl*>(tab2->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:553131 ->GetPrimaryFrameTree()
3132 .root();
W. James MacLeanb70fab82020-05-01 18:51:143133 SiteInstanceImpl* tab2_site_instance =
3134 tab2_root->current_frame_host()->GetSiteInstance();
3135 EXPECT_NE(tab1_site_instance, tab2_site_instance);
3136 EXPECT_NE(tab1_site_instance->GetIsolationContext().browsing_instance_id(),
3137 tab2_site_instance->GetIsolationContext().browsing_instance_id());
3138
3139 // Despite the non-isolated navigation only being at pending-commit when we
3140 // got the response for the isolated navigation, it should be properly
3141 // registered as non-isolated in its browsing instance.
W. James MacLeanb70fab82020-05-01 18:51:143142
W. James MacLean46cf26212020-10-01 16:43:373143 url::Origin isolated_origin = url::Origin::Create(isolated_origin_url);
3144 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
Ari Chivukula5350aad92021-08-10 02:42:243145 // Verify that `isolated origin` is in the non-opt-in list for tab1's
W. James MacLean46cf26212020-10-01 16:43:373146 // BrowsingInstance. We do this by requesting opt-in for the origin, then
W. James MacLeand42fa812021-11-18 22:59:263147 // verifying that it is denied by DetermineOriginAgentClusterIsolation.
W. James MacLean7f76c2202021-11-15 16:27:493148 EXPECT_FALSE(policy
W. James MacLeand42fa812021-11-18 22:59:263149 ->DetermineOriginAgentClusterIsolation(
W. James MacLean7f76c2202021-11-15 16:27:493150 tab1_site_instance->GetIsolationContext(),
3151 isolated_origin, MakeOACIsolationState(true))
3152 .requires_origin_keyed_process());
W. James MacLean46cf26212020-10-01 16:43:373153
Ari Chivukula5350aad92021-08-10 02:42:243154 // Verify that `isolated_origin` in tab2 is indeed isolated.
W. James MacLean7f76c2202021-11-15 16:27:493155 EXPECT_TRUE(policy
W. James MacLeand42fa812021-11-18 22:59:263156 ->DetermineOriginAgentClusterIsolation(
W. James MacLean7f76c2202021-11-15 16:27:493157 tab2_site_instance->GetIsolationContext(),
3158 isolated_origin, MakeOACIsolationState(false))
3159 .requires_origin_keyed_process());
W. James MacLeanb70fab82020-05-01 18:51:143160}
3161
3162// Helper class to navigate a second tab to a specified URL that requests opt-in
3163// origin isolation just before the first tab processes the next
3164// DidCommitProvisionalLoad message.
3165class InjectIsolationRequestingNavigation
3166 : public DidCommitNavigationInterceptor {
3167 public:
3168 InjectIsolationRequestingNavigation(
Domenic Denicola7dbd3d12021-01-08 21:26:563169 OriginIsolationOptInHeaderTest* test_framework,
W. James MacLeanb70fab82020-05-01 18:51:143170 WebContents* tab1_web_contents,
3171 Shell* tab2,
3172 const GURL& url)
3173 : DidCommitNavigationInterceptor(tab1_web_contents),
3174 test_framework_(test_framework),
3175 tab2_(tab2),
3176 url_(url) {}
3177
Sharon Yang034fbb722021-06-23 17:21:323178 InjectIsolationRequestingNavigation(
3179 const InjectIsolationRequestingNavigation&) = delete;
3180 InjectIsolationRequestingNavigation& operator=(
3181 const InjectIsolationRequestingNavigation&) = delete;
3182
W. James MacLeanb70fab82020-05-01 18:51:143183 bool was_called() { return was_called_; }
3184
3185 private:
3186 // DidCommitNavigationInterceptor implementation.
3187 bool WillProcessDidCommitNavigation(
3188 RenderFrameHost* render_frame_host,
3189 NavigationRequest* navigation_request,
arthursonzogni73fe3212020-11-17 13:24:073190 mojom::DidCommitProvisionalLoadParamsPtr*,
W. James MacLeanb70fab82020-05-01 18:51:143191 mojom::DidCommitProvisionalLoadInterfaceParamsPtr* interface_params)
3192 override {
3193 was_called_ = true;
3194
Ari Chivukula5350aad92021-08-10 02:42:243195 // Performa a navigation of `tab2_` to `url_`. `url_` should request
W. James MacLeanb70fab82020-05-01 18:51:143196 // isolation.
Domenic Denicola7dbd3d12021-01-08 21:26:563197 test_framework_->SetHeaderValue("?1");
Ali Hijazid87307d2022-11-07 20:15:033198 EXPECT_TRUE(NavigateToURL(tab2_, *url_));
W. James MacLeanb70fab82020-05-01 18:51:143199
3200 return true;
3201 }
3202
Jagadesh P1c8699c22023-11-13 14:09:373203 raw_ptr<OriginIsolationOptInHeaderTest> test_framework_ = nullptr;
3204 raw_ptr<Shell> tab2_ = nullptr;
Ali Hijazid87307d2022-11-07 20:15:033205 const raw_ref<const GURL> url_;
W. James MacLeanb70fab82020-05-01 18:51:143206 bool was_called_ = false;
W. James MacLeanb70fab82020-05-01 18:51:143207};
3208
Alison Gale81f4f2c72024-04-22 19:33:313209// TODO(crbug.com/40708791): flaky on Android builders since 2020-07-28.
Xiaohan Wang1ecfd002022-01-19 22:33:103210#if BUILDFLAG(IS_ANDROID)
Wei-Yin Chen (陳威尹)bbff5312020-07-29 08:21:073211#define MAYBE_FrameTreeTestBeforeDidCommit DISABLED_FrameTreeTestBeforeDidCommit
3212#else
3213#define MAYBE_FrameTreeTestBeforeDidCommit FrameTreeTestBeforeDidCommit
3214#endif
W. James MacLeanb70fab82020-05-01 18:51:143215// This test is similar to the one above, but exercises the pending navigation
3216// when it's at a different stage, namely between the CommitNavigation and
3217// DidCommitProvisionalLoad, rather than at WillProcessResponse.
Domenic Denicola7dbd3d12021-01-08 21:26:563218IN_PROC_BROWSER_TEST_F(OriginIsolationOptInHeaderTest,
Wei-Yin Chen (陳威尹)bbff5312020-07-29 08:21:073219 MAYBE_FrameTreeTestBeforeDidCommit) {
W. James MacLeanb70fab82020-05-01 18:51:143220 GURL isolated_origin_url(
3221 https_server()->GetURL("isolated.foo.com", "/isolate_origin"));
3222
Carlos Caballero15caeeb2021-10-27 09:57:553223 FrameTreeNode* tab1_root = web_contents()->GetPrimaryFrameTree().root();
W. James MacLeanb70fab82020-05-01 18:51:143224 // We use the following, slightly more verbose, code instead of
3225 // CreateBrowser() in order to avoid issues with NavigateToURL() in
3226 // InjectIsolationRequestingNavigation::WillProcessDidCommitNavigation()
3227 // getting stuck when it calls for WaitForLoadStop internally.
3228 Shell* tab2 =
3229 Shell::CreateNewWindow(shell()->web_contents()->GetBrowserContext(),
3230 GURL(), nullptr, gfx::Size());
3231
3232 InjectIsolationRequestingNavigation injector(this, web_contents(), tab2,
3233 isolated_origin_url);
Lukasz Anforowicz051314d2021-09-29 20:07:393234 {
3235 TestNavigationObserver tab1_navigation_observer(shell()->web_contents(), 1);
3236 tab1_navigation_observer.set_expected_initial_url(isolated_origin_url);
3237 shell()->LoadURL(isolated_origin_url);
3238
3239 // Waiting for DidNavigationFinished is sufficient to ensure that
3240 // `injector.was_called()`. We can't waiting for DidStopLoading, because
3241 // running a nested message loop in the injector confuses
3242 // TestNavigationObserver by changing the order of notifications.
3243 tab1_navigation_observer.WaitForNavigationFinished();
3244 }
W. James MacLeanb70fab82020-05-01 18:51:143245 EXPECT_TRUE(injector.was_called());
3246
3247 SiteInstanceImpl* tab1_site_instance =
3248 tab1_root->current_frame_host()->GetSiteInstance();
3249 FrameTreeNode* tab2_root = static_cast<WebContentsImpl*>(tab2->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:553250 ->GetPrimaryFrameTree()
3251 .root();
W. James MacLeanb70fab82020-05-01 18:51:143252 SiteInstanceImpl* tab2_site_instance =
3253 tab2_root->current_frame_host()->GetSiteInstance();
3254 EXPECT_NE(tab1_site_instance, tab2_site_instance);
3255 EXPECT_NE(tab1_site_instance->GetIsolationContext().browsing_instance_id(),
3256 tab2_site_instance->GetIsolationContext().browsing_instance_id());
3257
3258 // Despite the non-isolated navigation only being at pending-commit when we
3259 // got the response for the isolated navigation, it should be properly
3260 // registered as non-isolated in its browsing instance.
W. James MacLeanb70fab82020-05-01 18:51:143261 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
3262 url::Origin isolated_origin = url::Origin::Create(isolated_origin_url);
Ari Chivukula5350aad92021-08-10 02:42:243263 // Verify that `isolated origin` is in the non-opt-in list for tab1's
W. James MacLean46cf26212020-10-01 16:43:373264 // BrowsingInstance. We do this by requesting opt-in for the origin, then
3265 // verifying that it is denied by DoesOriginRequestOptInIsolation.
W. James MacLean7f76c2202021-11-15 16:27:493266 EXPECT_FALSE(policy
W. James MacLeand42fa812021-11-18 22:59:263267 ->DetermineOriginAgentClusterIsolation(
W. James MacLean7f76c2202021-11-15 16:27:493268 tab1_site_instance->GetIsolationContext(),
3269 isolated_origin, MakeOACIsolationState(true))
3270 .requires_origin_keyed_process());
W. James MacLeanb70fab82020-05-01 18:51:143271
Ari Chivukula5350aad92021-08-10 02:42:243272 // Verify that `isolated_origin` in tab2 is indeed isolated.
W. James MacLean7f76c2202021-11-15 16:27:493273 EXPECT_TRUE(policy
W. James MacLeand42fa812021-11-18 22:59:263274 ->DetermineOriginAgentClusterIsolation(
W. James MacLean7f76c2202021-11-15 16:27:493275 tab2_site_instance->GetIsolationContext(),
3276 isolated_origin, MakeOACIsolationState(false))
3277 .requires_origin_keyed_process());
W. James MacLeanb70fab82020-05-01 18:51:143278}
3279
W. James MacLeanf79c97e2019-05-02 20:35:463280class StrictOriginIsolationTest : public IsolatedOriginTestBase {
3281 public:
Sharon Yang034fbb722021-06-23 17:21:323282 StrictOriginIsolationTest() = default;
3283 ~StrictOriginIsolationTest() override = default;
3284
3285 StrictOriginIsolationTest(const StrictOriginIsolationTest&) = delete;
3286 StrictOriginIsolationTest& operator=(const StrictOriginIsolationTest&) =
3287 delete;
W. James MacLeanf79c97e2019-05-02 20:35:463288
3289 void SetUpCommandLine(base::CommandLine* command_line) override {
3290 IsolatedOriginTestBase::SetUpCommandLine(command_line);
3291 ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
3292
3293 // This is needed for this test to run properly on platforms where
3294 // --site-per-process isn't the default, such as Android.
3295 IsolateAllSitesForTesting(command_line);
3296 feature_list_.InitAndEnableFeature(features::kStrictOriginIsolation);
3297 }
3298
3299 void SetUpOnMainThread() override {
3300 host_resolver()->AddRule("*", "127.0.0.1");
3301 embedded_test_server()->StartAcceptingConnections();
3302 }
3303
Ari Chivukula5350aad92021-08-10 02:42:243304 // Helper function that creates an http URL for `host` that includes the test
W. James MacLeane84fa112020-07-14 17:25:543305 // server's port and returns the strict ProcessLock for that URL.
3306 ProcessLock GetStrictProcessLockForHost(const std::string& host) {
3307 return GetStrictProcessLock(embedded_test_server()->GetURL(host, "/"));
Aaron Colwell80f85bb2020-05-19 01:55:063308 }
3309
W. James MacLeanf79c97e2019-05-02 20:35:463310 private:
3311 base::test::ScopedFeatureList feature_list_;
W. James MacLeanf79c97e2019-05-02 20:35:463312};
3313
3314IN_PROC_BROWSER_TEST_F(StrictOriginIsolationTest, SubframesAreIsolated) {
3315 GURL test_url(embedded_test_server()->GetURL(
3316 "foo.com",
3317 "/cross_site_iframe_factory.html?"
3318 "foo.com(mail.foo.com,bar.foo.com(foo.com),foo.com)"));
3319 EXPECT_TRUE(NavigateToURL(shell(), test_url));
Dave Tapuska4eea4112021-09-16 15:49:213320 EXPECT_EQ(5u, CollectAllRenderFrameHosts(shell()->web_contents()).size());
W. James MacLeanf79c97e2019-05-02 20:35:463321
3322 // Make sure we have three separate processes.
Carlos Caballero15caeeb2021-10-27 09:57:553323 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
W. James MacLeanf79c97e2019-05-02 20:35:463324 RenderFrameHost* main_frame = root->current_frame_host();
Emily Andrewsd15fd762024-12-10 20:41:543325 int main_frame_id = main_frame->GetProcess()->GetDeprecatedID();
W. James MacLeanf79c97e2019-05-02 20:35:463326 RenderFrameHost* child_frame0 = root->child_at(0)->current_frame_host();
W. James MacLeanf79c97e2019-05-02 20:35:463327 RenderFrameHost* child_frame1 = root->child_at(1)->current_frame_host();
W. James MacLeanf79c97e2019-05-02 20:35:463328 RenderFrameHost* child_frame2 = root->child_at(2)->current_frame_host();
W. James MacLeanf79c97e2019-05-02 20:35:463329 RenderFrameHost* grandchild_frame0 =
3330 root->child_at(1)->child_at(0)->current_frame_host();
Emily Andrewsd15fd762024-12-10 20:41:543331 EXPECT_NE(main_frame_id, child_frame0->GetProcess()->GetDeprecatedID());
3332 EXPECT_NE(main_frame_id, child_frame1->GetProcess()->GetDeprecatedID());
3333 EXPECT_EQ(main_frame_id, child_frame2->GetProcess()->GetDeprecatedID());
3334 EXPECT_EQ(main_frame_id, grandchild_frame0->GetProcess()->GetDeprecatedID());
W. James MacLeanf79c97e2019-05-02 20:35:463335
W. James MacLeane84fa112020-07-14 17:25:543336 EXPECT_EQ(GetStrictProcessLockForHost("foo.com"),
Sharon Yang57481e52021-12-01 00:05:223337 main_frame->GetProcess()->GetProcessLock());
W. James MacLeane84fa112020-07-14 17:25:543338 EXPECT_EQ(GetStrictProcessLockForHost("mail.foo.com"),
Sharon Yang57481e52021-12-01 00:05:223339 child_frame0->GetProcess()->GetProcessLock());
W. James MacLeane84fa112020-07-14 17:25:543340 EXPECT_EQ(GetStrictProcessLockForHost("bar.foo.com"),
Sharon Yang57481e52021-12-01 00:05:223341 child_frame1->GetProcess()->GetProcessLock());
W. James MacLeane84fa112020-07-14 17:25:543342 EXPECT_EQ(GetStrictProcessLockForHost("foo.com"),
Sharon Yang57481e52021-12-01 00:05:223343 child_frame2->GetProcess()->GetProcessLock());
W. James MacLeane84fa112020-07-14 17:25:543344 EXPECT_EQ(GetStrictProcessLockForHost("foo.com"),
Sharon Yang57481e52021-12-01 00:05:223345 grandchild_frame0->GetProcess()->GetProcessLock());
W. James MacLeanf79c97e2019-05-02 20:35:463346
3347 // Navigate child_frame1 to a new origin ... it should get its own process.
3348 FrameTreeNode* child_frame2_node = root->child_at(2);
3349 GURL foo_url(embedded_test_server()->GetURL("www.foo.com", "/title1.html"));
W. James MacLeane84fa112020-07-14 17:25:543350 const auto expected_foo_lock = GetStrictProcessLock(foo_url);
Lukasz Anforowicz69c25dfd2020-11-12 21:50:203351 EXPECT_TRUE(NavigateToURLFromRenderer(child_frame2_node, foo_url));
W. James MacLeanf79c97e2019-05-02 20:35:463352 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
3353 child_frame2_node->current_frame_host()->GetSiteInstance());
3354 // The old RenderFrameHost for subframe3 will no longer be valid, so get the
3355 // new one.
3356 child_frame2 = root->child_at(2)->current_frame_host();
Emily Andrewsd15fd762024-12-10 20:41:543357 EXPECT_NE(main_frame->GetProcess()->GetDeprecatedID(),
3358 child_frame2->GetProcess()->GetDeprecatedID());
Sharon Yang57481e52021-12-01 00:05:223359 EXPECT_EQ(expected_foo_lock, child_frame2->GetProcess()->GetProcessLock());
W. James MacLeanf79c97e2019-05-02 20:35:463360}
3361
3362IN_PROC_BROWSER_TEST_F(StrictOriginIsolationTest, MainframesAreIsolated) {
3363 GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
W. James MacLeane84fa112020-07-14 17:25:543364 const auto expected_foo_lock = GetStrictProcessLock(foo_url);
W. James MacLeanf79c97e2019-05-02 20:35:463365 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
Dave Tapuska4eea4112021-09-16 15:49:213366 EXPECT_EQ(1u, CollectAllRenderFrameHosts(shell()->web_contents()).size());
W. James MacLeanf79c97e2019-05-02 20:35:463367 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
3368
Dave Tapuska327c06c92022-06-13 20:31:513369 auto foo_process_id =
Emily Andrewsd15fd762024-12-10 20:41:543370 web_contents()->GetPrimaryMainFrame()->GetProcess()->GetDeprecatedID();
Aaron Colwell80f85bb2020-05-19 01:55:063371 SiteInstanceImpl* foo_site_instance = web_contents()->GetSiteInstance();
Sharon Yang2c077a72021-11-30 02:27:583372 EXPECT_EQ(expected_foo_lock,
3373 ProcessLock::FromSiteInfo(foo_site_instance->GetSiteInfo()));
3374 EXPECT_EQ(ProcessLock::FromSiteInfo(foo_site_instance->GetSiteInfo()),
W. James MacLeane84fa112020-07-14 17:25:543375 policy->GetProcessLock(foo_process_id));
W. James MacLeanf79c97e2019-05-02 20:35:463376
Aaron Colwell80f85bb2020-05-19 01:55:063377 GURL sub_foo_url =
3378 embedded_test_server()->GetURL("sub.foo.com", "/title1.html");
W. James MacLeane84fa112020-07-14 17:25:543379 const auto expected_sub_foo_lock = GetStrictProcessLock(sub_foo_url);
Aaron Colwell80f85bb2020-05-19 01:55:063380 EXPECT_TRUE(NavigateToURL(shell(), sub_foo_url));
Emily Andrewsd15fd762024-12-10 20:41:543381 auto sub_foo_process_id = shell()
3382 ->web_contents()
3383 ->GetPrimaryMainFrame()
3384 ->GetProcess()
3385 ->GetDeprecatedID();
Aaron Colwell80f85bb2020-05-19 01:55:063386 SiteInstanceImpl* sub_foo_site_instance = web_contents()->GetSiteInstance();
Sharon Yang2c077a72021-11-30 02:27:583387 EXPECT_EQ(expected_sub_foo_lock,
3388 ProcessLock::FromSiteInfo(sub_foo_site_instance->GetSiteInfo()));
3389 EXPECT_EQ(ProcessLock::FromSiteInfo(sub_foo_site_instance->GetSiteInfo()),
W. James MacLeane84fa112020-07-14 17:25:543390 policy->GetProcessLock(sub_foo_process_id));
W. James MacLeanf79c97e2019-05-02 20:35:463391
3392 EXPECT_NE(foo_process_id, sub_foo_process_id);
3393 EXPECT_NE(foo_site_instance->GetSiteURL(),
3394 sub_foo_site_instance->GetSiteURL());
3395
3396 // Now verify with a renderer-initiated navigation.
3397 GURL another_foo_url(
3398 embedded_test_server()->GetURL("another.foo.com", "/title2.html"));
W. James MacLeane84fa112020-07-14 17:25:543399 const auto expected_another_foo_lock = GetStrictProcessLock(another_foo_url);
W. James MacLeanf79c97e2019-05-02 20:35:463400 EXPECT_TRUE(NavigateToURLFromRenderer(shell(), another_foo_url));
Emily Andrewsd15fd762024-12-10 20:41:543401 auto another_foo_process_id = shell()
3402 ->web_contents()
3403 ->GetPrimaryMainFrame()
3404 ->GetProcess()
3405 ->GetDeprecatedID();
Aaron Colwell80f85bb2020-05-19 01:55:063406 SiteInstanceImpl* another_foo_site_instance =
3407 web_contents()->GetSiteInstance();
W. James MacLeanf79c97e2019-05-02 20:35:463408 EXPECT_NE(another_foo_process_id, sub_foo_process_id);
3409 EXPECT_NE(another_foo_process_id, foo_process_id);
Sharon Yang2c077a72021-11-30 02:27:583410 EXPECT_EQ(
3411 expected_another_foo_lock,
3412 ProcessLock::FromSiteInfo(another_foo_site_instance->GetSiteInfo()));
3413 EXPECT_EQ(ProcessLock::FromSiteInfo(another_foo_site_instance->GetSiteInfo()),
W. James MacLeane84fa112020-07-14 17:25:543414 policy->GetProcessLock(another_foo_process_id));
W. James MacLeanf79c97e2019-05-02 20:35:463415 EXPECT_NE(another_foo_site_instance, foo_site_instance);
Aaron Colwell80f85bb2020-05-19 01:55:063416
3417 EXPECT_NE(expected_foo_lock, expected_sub_foo_lock);
3418 EXPECT_NE(expected_sub_foo_lock, expected_another_foo_lock);
3419 EXPECT_NE(expected_another_foo_lock, expected_foo_lock);
W. James MacLeanf79c97e2019-05-02 20:35:463420}
3421
Alex Moshchuke456cf552020-08-19 17:09:293422// Ensure that navigations across two URLs that resolve to the same effective
3423// URL won't result in a renderer kill with strict origin isolation. See
3424// https://crbug.com/961386.
3425IN_PROC_BROWSER_TEST_F(StrictOriginIsolationTest,
3426 NavigateToURLsWithSameEffectiveURL) {
3427 GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
3428 GURL bar_url(embedded_test_server()->GetURL("bar.com", "/title1.html"));
3429 GURL app_url(GetWebUIURL("translated"));
3430
Ari Chivukula5350aad92021-08-10 02:42:243431 // Set up effective URL translation that maps both `foo_url` and `bar_url` to
3432 // `app_url`.
Scott Violet99861992023-02-08 01:20:123433 EffectiveURLContentBrowserTestContentBrowserClient modified_client(
Alex Moshchuke456cf552020-08-19 17:09:293434 false /* requires_dedicated_process */);
3435 modified_client.AddTranslation(foo_url, app_url);
3436 modified_client.AddTranslation(bar_url, app_url);
Alex Moshchuke456cf552020-08-19 17:09:293437
Ari Chivukula5350aad92021-08-10 02:42:243438 // Calculate the expected SiteInfo for each URL. Both `foo_url` and
3439 // `bar_url` should have a site URL of `app_url`, but the process locks
Alex Moshchuke456cf552020-08-19 17:09:293440 // should be foo.com and bar.com.
Aaron Colwell9d0f9392021-02-11 21:51:523441 SiteInfo foo_site_info = SiteInfo::CreateForTesting(
Alex Moshchuke456cf552020-08-19 17:09:293442 web_contents()->GetSiteInstance()->GetIsolationContext(), foo_url);
3443 EXPECT_EQ(app_url, foo_site_info.site_url());
Mike West800532c2021-10-14 09:26:523444 EXPECT_EQ(foo_url.DeprecatedGetOriginAsURL(),
3445 foo_site_info.process_lock_url());
Aaron Colwell9d0f9392021-02-11 21:51:523446 SiteInfo bar_site_info = SiteInfo::CreateForTesting(
Alex Moshchuke456cf552020-08-19 17:09:293447 web_contents()->GetSiteInstance()->GetIsolationContext(), bar_url);
3448 EXPECT_EQ(app_url, bar_site_info.site_url());
Mike West800532c2021-10-14 09:26:523449 EXPECT_EQ(bar_url.DeprecatedGetOriginAsURL(),
3450 bar_site_info.process_lock_url());
Alex Moshchuke456cf552020-08-19 17:09:293451 EXPECT_EQ(foo_site_info.site_url(), bar_site_info.site_url());
3452
3453 // Navigate to foo_url and then to bar_url. Verify that we end up with
3454 // correct SiteInfo in each case.
3455 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
3456 scoped_refptr<SiteInstanceImpl> foo_site_instance =
3457 web_contents()->GetSiteInstance();
3458 EXPECT_EQ(foo_site_info, foo_site_instance->GetSiteInfo());
3459
3460 EXPECT_TRUE(NavigateToURL(shell(), bar_url));
3461 scoped_refptr<SiteInstanceImpl> bar_site_instance =
3462 web_contents()->GetSiteInstance();
3463 EXPECT_EQ(bar_site_info, bar_site_instance->GetSiteInfo());
3464
3465 // Verify that the SiteInstances and processes are different. In
3466 // https://crbug.com/961386, we didn't swap processes for the second
3467 // navigation, leading to renderer kills.
3468 EXPECT_NE(foo_site_instance.get(), bar_site_instance.get());
Jiacheng Guocd621762025-06-17 01:14:323469 EXPECT_NE(foo_site_instance->GetOrCreateProcessForTesting(),
Jiacheng Guo99c6fb42025-02-03 08:11:553470 bar_site_instance->GetProcess());
Alex Moshchuke456cf552020-08-19 17:09:293471
3472 // Navigate to another site, then repeat this test with a redirect from
3473 // foo.com to bar.com. The navigation should throw away the speculative RFH
3474 // created for foo.com and should commit in a process locked to bar.com.
3475 EXPECT_TRUE(NavigateToURL(
3476 shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
3477 GURL redirect_url(embedded_test_server()->GetURL(
3478 "foo.com", "/server-redirect?" + bar_url.spec()));
3479 modified_client.AddTranslation(redirect_url, app_url);
3480 EXPECT_TRUE(NavigateToURL(shell(), redirect_url, bar_url));
3481 EXPECT_EQ(bar_site_info, web_contents()->GetSiteInstance()->GetSiteInfo());
Alex Moshchuke456cf552020-08-19 17:09:293482}
3483
alexmos3b9ad102017-05-26 23:41:083484// Check that navigating a main frame from an non-isolated origin to an
3485// isolated origin and vice versa swaps processes and uses a new SiteInstance,
Alex Moshchuk7e26eca2018-03-03 01:34:293486// both for renderer-initiated and browser-initiated navigations.
alexmos3b9ad102017-05-26 23:41:083487IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, MainFrameNavigation) {
3488 GURL unisolated_url(
3489 embedded_test_server()->GetURL("www.foo.com", "/title1.html"));
3490 GURL isolated_url(
3491 embedded_test_server()->GetURL("isolated.foo.com", "/title2.html"));
3492
3493 EXPECT_TRUE(NavigateToURL(shell(), unisolated_url));
3494
3495 // Open a same-site popup to keep the www.foo.com process alive.
3496 Shell* popup = OpenPopup(shell(), GURL(url::kAboutBlankURL), "foo");
3497 SiteInstance* unisolated_instance =
Dave Tapuska327c06c92022-06-13 20:31:513498 popup->web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
alexmos3b9ad102017-05-26 23:41:083499 RenderProcessHost* unisolated_process =
Dave Tapuska327c06c92022-06-13 20:31:513500 popup->web_contents()->GetPrimaryMainFrame()->GetProcess();
alexmos3b9ad102017-05-26 23:41:083501
Alex Moshchuk7e26eca2018-03-03 01:34:293502 // Go to isolated.foo.com with a renderer-initiated navigation.
3503 EXPECT_TRUE(NavigateToURLFromRenderer(web_contents(), isolated_url));
alexmos3b9ad102017-05-26 23:41:083504 scoped_refptr<SiteInstance> isolated_instance =
3505 web_contents()->GetSiteInstance();
Alex Moshchuk7e26eca2018-03-03 01:34:293506 EXPECT_EQ(isolated_instance, web_contents()->GetSiteInstance());
Dave Tapuska327c06c92022-06-13 20:31:513507 EXPECT_NE(unisolated_process,
3508 web_contents()->GetPrimaryMainFrame()->GetProcess());
alexmos3b9ad102017-05-26 23:41:083509
3510 // The site URL for isolated.foo.com should be the full origin rather than
3511 // scheme and eTLD+1.
Lukasz Anforowicz25420932018-12-18 20:59:223512 EXPECT_EQ(GURL("http://isolated.foo.com/"), isolated_instance->GetSiteURL());
alexmos3b9ad102017-05-26 23:41:083513
Alex Moshchuk7e26eca2018-03-03 01:34:293514 // Now use a renderer-initiated navigation to go to an unisolated origin,
Ari Chivukula5350aad92021-08-10 02:42:243515 // www.foo.com. This should end up back in the `popup`'s process.
Alex Moshchuk7e26eca2018-03-03 01:34:293516 EXPECT_TRUE(NavigateToURLFromRenderer(web_contents(), unisolated_url));
alexmos3b9ad102017-05-26 23:41:083517 EXPECT_EQ(unisolated_instance, web_contents()->GetSiteInstance());
Dave Tapuska327c06c92022-06-13 20:31:513518 EXPECT_EQ(unisolated_process,
3519 web_contents()->GetPrimaryMainFrame()->GetProcess());
alexmos3b9ad102017-05-26 23:41:083520
Alex Moshchuk7e26eca2018-03-03 01:34:293521 // Now, perform a browser-initiated navigation to an isolated origin and
3522 // ensure that this ends up in a new process and SiteInstance for
3523 // isolated.foo.com.
3524 EXPECT_TRUE(NavigateToURL(shell(), isolated_url));
3525 EXPECT_NE(web_contents()->GetSiteInstance(), unisolated_instance);
Dave Tapuska327c06c92022-06-13 20:31:513526 EXPECT_NE(web_contents()->GetPrimaryMainFrame()->GetProcess(),
3527 unisolated_process);
alexmos3b9ad102017-05-26 23:41:083528
3529 // Go back to www.foo.com: this should end up in the unisolated process.
3530 {
3531 TestNavigationObserver back_observer(web_contents());
3532 web_contents()->GetController().GoBack();
3533 back_observer.Wait();
3534 }
3535
3536 EXPECT_EQ(unisolated_instance, web_contents()->GetSiteInstance());
Dave Tapuska327c06c92022-06-13 20:31:513537 EXPECT_EQ(unisolated_process,
3538 web_contents()->GetPrimaryMainFrame()->GetProcess());
alexmos3b9ad102017-05-26 23:41:083539
3540 // Go back again. This should go to isolated.foo.com in an isolated process.
3541 {
3542 TestNavigationObserver back_observer(web_contents());
3543 web_contents()->GetController().GoBack();
3544 back_observer.Wait();
3545 }
3546
3547 EXPECT_EQ(isolated_instance, web_contents()->GetSiteInstance());
Dave Tapuska327c06c92022-06-13 20:31:513548 EXPECT_NE(unisolated_process,
3549 web_contents()->GetPrimaryMainFrame()->GetProcess());
alexmos3b9ad102017-05-26 23:41:083550
3551 // Do a renderer-initiated navigation from isolated.foo.com to another
3552 // isolated origin and ensure there is a different isolated process.
3553 GURL second_isolated_url(
3554 embedded_test_server()->GetURL("isolated.bar.com", "/title3.html"));
Alex Moshchuk7e26eca2018-03-03 01:34:293555 EXPECT_TRUE(NavigateToURLFromRenderer(web_contents(), second_isolated_url));
Lukasz Anforowicz25420932018-12-18 20:59:223556 EXPECT_EQ(GURL("http://isolated.bar.com/"),
alexmos3b9ad102017-05-26 23:41:083557 web_contents()->GetSiteInstance()->GetSiteURL());
3558 EXPECT_NE(isolated_instance, web_contents()->GetSiteInstance());
3559 EXPECT_NE(unisolated_instance, web_contents()->GetSiteInstance());
3560}
3561
3562// Check that opening a popup for an isolated origin puts it into a new process
3563// and its own SiteInstance.
3564IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, Popup) {
3565 GURL unisolated_url(
3566 embedded_test_server()->GetURL("foo.com", "/title1.html"));
3567 GURL isolated_url(
3568 embedded_test_server()->GetURL("isolated.foo.com", "/title2.html"));
3569
3570 EXPECT_TRUE(NavigateToURL(shell(), unisolated_url));
3571
3572 // Open a popup to a URL with an isolated origin and ensure that there was a
3573 // process swap.
3574 Shell* popup = OpenPopup(shell(), isolated_url, "foo");
3575
3576 EXPECT_NE(shell()->web_contents()->GetSiteInstance(),
3577 popup->web_contents()->GetSiteInstance());
3578
3579 // The popup's site URL should match the full isolated origin.
Lukasz Anforowicz25420932018-12-18 20:59:223580 EXPECT_EQ(GURL("http://isolated.foo.com/"),
alexmos3b9ad102017-05-26 23:41:083581 popup->web_contents()->GetSiteInstance()->GetSiteURL());
3582
3583 // Now open a second popup from an isolated origin to a URL with an
3584 // unisolated origin and ensure that there was another process swap.
3585 Shell* popup2 = OpenPopup(popup, unisolated_url, "bar");
3586 EXPECT_EQ(shell()->web_contents()->GetSiteInstance(),
3587 popup2->web_contents()->GetSiteInstance());
3588 EXPECT_NE(popup->web_contents()->GetSiteInstance(),
3589 popup2->web_contents()->GetSiteInstance());
3590}
3591
3592// Check that navigating a subframe to an isolated origin puts the subframe
3593// into an OOPIF and its own SiteInstance. Also check that the isolated
3594// frame's subframes also end up in correct SiteInstance.
3595IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, Subframe) {
3596 GURL top_url(
3597 embedded_test_server()->GetURL("www.foo.com", "/page_with_iframe.html"));
3598 EXPECT_TRUE(NavigateToURL(shell(), top_url));
3599
3600 GURL isolated_url(embedded_test_server()->GetURL("isolated.foo.com",
3601 "/page_with_iframe.html"));
3602
Carlos Caballero15caeeb2021-10-27 09:57:553603 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmos3b9ad102017-05-26 23:41:083604 FrameTreeNode* child = root->child_at(0);
3605
3606 NavigateIframeToURL(web_contents(), "test_iframe", isolated_url);
3607 EXPECT_EQ(child->current_url(), isolated_url);
3608
3609 // Verify that the child frame is an OOPIF with a different SiteInstance.
3610 EXPECT_NE(web_contents()->GetSiteInstance(),
3611 child->current_frame_host()->GetSiteInstance());
3612 EXPECT_TRUE(child->current_frame_host()->IsCrossProcessSubframe());
Lukasz Anforowicz25420932018-12-18 20:59:223613 EXPECT_EQ(GURL("http://isolated.foo.com/"),
alexmos3b9ad102017-05-26 23:41:083614 child->current_frame_host()->GetSiteInstance()->GetSiteURL());
3615
3616 // Verify that the isolated frame's subframe (which starts out at a relative
3617 // path) is kept in the isolated parent's SiteInstance.
3618 FrameTreeNode* grandchild = child->child_at(0);
3619 EXPECT_EQ(child->current_frame_host()->GetSiteInstance(),
3620 grandchild->current_frame_host()->GetSiteInstance());
3621
3622 // Navigating the grandchild to www.foo.com should put it into the top
3623 // frame's SiteInstance.
3624 GURL non_isolated_url(
3625 embedded_test_server()->GetURL("www.foo.com", "/title3.html"));
3626 TestFrameNavigationObserver observer(grandchild);
Avi Drissmanc91bd8e2021-04-19 23:58:443627 EXPECT_TRUE(
3628 ExecJs(grandchild, "location.href = '" + non_isolated_url.spec() + "';"));
alexmos3b9ad102017-05-26 23:41:083629 observer.Wait();
3630 EXPECT_EQ(non_isolated_url, grandchild->current_url());
3631
3632 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
3633 grandchild->current_frame_host()->GetSiteInstance());
3634 EXPECT_NE(child->current_frame_host()->GetSiteInstance(),
3635 grandchild->current_frame_host()->GetSiteInstance());
3636}
3637
3638// Check that when an non-isolated origin foo.com embeds a subframe from an
3639// isolated origin, which then navigates to a non-isolated origin bar.com,
3640// bar.com goes back to the main frame's SiteInstance. See
3641// https://crbug.com/711006.
3642IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,
3643 NoOOPIFWhenIsolatedOriginNavigatesToNonIsolatedOrigin) {
Jagadesh P1c8699c22023-11-13 14:09:373644 if (AreAllSitesIsolatedForTesting()) {
alexmos3b9ad102017-05-26 23:41:083645 return;
Jagadesh P1c8699c22023-11-13 14:09:373646 }
alexmos3b9ad102017-05-26 23:41:083647
3648 GURL top_url(
3649 embedded_test_server()->GetURL("www.foo.com", "/page_with_iframe.html"));
3650 EXPECT_TRUE(NavigateToURL(shell(), top_url));
3651
Carlos Caballero15caeeb2021-10-27 09:57:553652 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmos3b9ad102017-05-26 23:41:083653 FrameTreeNode* child = root->child_at(0);
3654
3655 GURL isolated_url(embedded_test_server()->GetURL("isolated.foo.com",
3656 "/page_with_iframe.html"));
3657
3658 NavigateIframeToURL(web_contents(), "test_iframe", isolated_url);
3659 EXPECT_EQ(isolated_url, child->current_url());
3660
3661 // Verify that the child frame is an OOPIF with a different SiteInstance.
3662 EXPECT_NE(web_contents()->GetSiteInstance(),
3663 child->current_frame_host()->GetSiteInstance());
3664 EXPECT_TRUE(child->current_frame_host()->IsCrossProcessSubframe());
Lukasz Anforowicz25420932018-12-18 20:59:223665 EXPECT_EQ(GURL("http://isolated.foo.com/"),
alexmos3b9ad102017-05-26 23:41:083666 child->current_frame_host()->GetSiteInstance()->GetSiteURL());
3667
Sharon Yang6a61229f2025-02-11 17:56:543668 // Navigate the child frame cross-site, but to a non-isolated origin. Without
3669 // full site isolation, this should bring the subframe back into the main
3670 // frame's SiteInstance. With full site isolation or another mode where a
3671 // SiteInstance is not allowed to contain multiple sites, we expect the
3672 // SiteInstances to be different. In all cases though we expect the
3673 // navigation to end up in the same process.
alexmos3b9ad102017-05-26 23:41:083674 GURL bar_url(embedded_test_server()->GetURL("bar.com", "/title1.html"));
Alex Moshchuk8e5c1952019-01-15 03:39:503675 EXPECT_FALSE(IsIsolatedOrigin(bar_url));
alexmos3b9ad102017-05-26 23:41:083676 NavigateIframeToURL(web_contents(), "test_iframe", bar_url);
Aaron Colwell5fb878042020-12-17 19:48:443677
3678 if (AreStrictSiteInstancesEnabled()) {
3679 EXPECT_NE(web_contents()->GetSiteInstance(),
3680 child->current_frame_host()->GetSiteInstance());
3681 } else {
3682 EXPECT_EQ(web_contents()->GetSiteInstance(),
3683 child->current_frame_host()->GetSiteInstance());
3684 }
3685 EXPECT_EQ(web_contents()->GetSiteInstance()->GetProcess(),
3686 child->current_frame_host()->GetSiteInstance()->GetProcess());
alexmos3b9ad102017-05-26 23:41:083687}
3688
alexmos7b7555fc2017-06-01 22:58:323689// Check that a new isolated origin subframe will attempt to reuse an existing
3690// process for that isolated origin, even across BrowsingInstances. Also check
3691// that main frame navigations to an isolated origin keep using the default
3692// process model and do not reuse existing processes.
3693IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, SubframeReusesExistingProcess) {
3694 GURL top_url(
3695 embedded_test_server()->GetURL("www.foo.com", "/page_with_iframe.html"));
3696 EXPECT_TRUE(NavigateToURL(shell(), top_url));
Carlos Caballero15caeeb2021-10-27 09:57:553697 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmos7b7555fc2017-06-01 22:58:323698 FrameTreeNode* child = root->child_at(0);
3699
3700 // Open an unrelated tab in a separate BrowsingInstance, and navigate it to
3701 // to an isolated origin. This SiteInstance should have a default process
3702 // reuse policy - only subframes attempt process reuse.
3703 GURL isolated_url(embedded_test_server()->GetURL("isolated.foo.com",
3704 "/page_with_iframe.html"));
3705 Shell* second_shell = CreateBrowser();
3706 EXPECT_TRUE(NavigateToURL(second_shell, isolated_url));
3707 scoped_refptr<SiteInstanceImpl> second_shell_instance =
Dave Tapuska327c06c92022-06-13 20:31:513708 static_cast<SiteInstanceImpl*>(second_shell->web_contents()
3709 ->GetPrimaryMainFrame()
3710 ->GetSiteInstance());
alexmos7b7555fc2017-06-01 22:58:323711 EXPECT_FALSE(second_shell_instance->IsRelatedSiteInstance(
3712 root->current_frame_host()->GetSiteInstance()));
3713 RenderProcessHost* isolated_process = second_shell_instance->GetProcess();
Charlie Reis9ce0ed222024-09-05 22:05:263714 EXPECT_EQ(ProcessReusePolicy::DEFAULT,
alexmos7b7555fc2017-06-01 22:58:323715 second_shell_instance->process_reuse_policy());
3716
3717 // Now navigate the first tab's subframe to an isolated origin. See that it
Ari Chivukula5350aad92021-08-10 02:42:243718 // reuses the existing `isolated_process`.
alexmos7b7555fc2017-06-01 22:58:323719 NavigateIframeToURL(web_contents(), "test_iframe", isolated_url);
3720 EXPECT_EQ(isolated_url, child->current_url());
3721 EXPECT_EQ(isolated_process, child->current_frame_host()->GetProcess());
3722 EXPECT_EQ(
Charlie Reis9ce0ed222024-09-05 22:05:263723 ProcessReusePolicy::REUSE_PENDING_OR_COMMITTED_SITE_SUBFRAME,
alexmos7b7555fc2017-06-01 22:58:323724 child->current_frame_host()->GetSiteInstance()->process_reuse_policy());
3725
3726 EXPECT_TRUE(child->current_frame_host()->IsCrossProcessSubframe());
Lukasz Anforowicz25420932018-12-18 20:59:223727 EXPECT_EQ(GURL("http://isolated.foo.com/"),
alexmos7b7555fc2017-06-01 22:58:323728 child->current_frame_host()->GetSiteInstance()->GetSiteURL());
3729
3730 // The subframe's SiteInstance should still be different from second_shell's
3731 // SiteInstance, and they should be in separate BrowsingInstances.
3732 EXPECT_NE(second_shell_instance,
3733 child->current_frame_host()->GetSiteInstance());
3734 EXPECT_FALSE(second_shell_instance->IsRelatedSiteInstance(
3735 child->current_frame_host()->GetSiteInstance()));
3736
3737 // Navigate the second tab to a normal URL with a same-site subframe. This
3738 // leaves only the first tab's subframe in the isolated origin process.
3739 EXPECT_TRUE(NavigateToURL(second_shell, top_url));
3740 EXPECT_NE(isolated_process,
Dave Tapuska327c06c92022-06-13 20:31:513741 second_shell->web_contents()->GetPrimaryMainFrame()->GetProcess());
alexmos7b7555fc2017-06-01 22:58:323742
3743 // Navigate the second tab's subframe to an isolated origin, and check that
3744 // this new subframe reuses the isolated process of the subframe in the first
3745 // tab, even though the two are in separate BrowsingInstances.
3746 NavigateIframeToURL(second_shell->web_contents(), "test_iframe",
3747 isolated_url);
3748 FrameTreeNode* second_subframe =
3749 static_cast<WebContentsImpl*>(second_shell->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:553750 ->GetPrimaryFrameTree()
3751 .root()
alexmos7b7555fc2017-06-01 22:58:323752 ->child_at(0);
3753 EXPECT_EQ(isolated_process,
3754 second_subframe->current_frame_host()->GetProcess());
3755 EXPECT_NE(child->current_frame_host()->GetSiteInstance(),
3756 second_subframe->current_frame_host()->GetSiteInstance());
3757
3758 // Open a third, unrelated tab, navigate it to an isolated origin, and check
3759 // that its main frame doesn't share a process with the existing isolated
3760 // subframes.
3761 Shell* third_shell = CreateBrowser();
3762 EXPECT_TRUE(NavigateToURL(third_shell, isolated_url));
3763 SiteInstanceImpl* third_shell_instance = static_cast<SiteInstanceImpl*>(
Dave Tapuska327c06c92022-06-13 20:31:513764 third_shell->web_contents()->GetPrimaryMainFrame()->GetSiteInstance());
alexmos7b7555fc2017-06-01 22:58:323765 EXPECT_NE(third_shell_instance,
3766 second_subframe->current_frame_host()->GetSiteInstance());
3767 EXPECT_NE(third_shell_instance,
3768 child->current_frame_host()->GetSiteInstance());
3769 EXPECT_NE(third_shell_instance->GetProcess(), isolated_process);
3770}
3771
Alex Moshchuk46a28e9a2018-02-01 06:11:513772// Check that when a non-isolated-origin page opens a popup, navigates it
3773// to an isolated origin, and then the popup navigates to a third non-isolated
3774// origin and finally back to its opener's origin, the popup and the opener
3775// iframe end up in the same process and can script each other:
3776//
3777// foo.com
3778// |
3779// window.open()
3780// |
3781// V
3782// about:blank -> isolated.foo.com -> bar.com -> foo.com
3783//
3784// This is a variant of PopupNavigatesToIsolatedOriginAndBack where the popup
3785// navigates to a third site before coming back to the opener's site. See
3786// https://crbug.com/807184.
3787IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,
3788 PopupNavigatesToIsolatedOriginThenToAnotherSiteAndBack) {
3789 // Start on www.foo.com.
3790 GURL foo_url(embedded_test_server()->GetURL("www.foo.com", "/title1.html"));
3791 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
Carlos Caballero15caeeb2021-10-27 09:57:553792 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Alex Moshchuk46a28e9a2018-02-01 06:11:513793
3794 // Open a blank popup.
3795 ShellAddedObserver new_shell_observer;
Avi Drissmanc91bd8e2021-04-19 23:58:443796 EXPECT_TRUE(ExecJs(root, "window.w = window.open();"));
Alex Moshchuk46a28e9a2018-02-01 06:11:513797 Shell* new_shell = new_shell_observer.GetShell();
3798
3799 // Have the opener navigate the popup to an isolated origin.
3800 GURL isolated_url(
3801 embedded_test_server()->GetURL("isolated.foo.com", "/title1.html"));
3802 {
3803 TestNavigationManager manager(new_shell->web_contents(), isolated_url);
Avi Drissmanc91bd8e2021-04-19 23:58:443804 EXPECT_TRUE(ExecJs(
Alex Moshchuk46a28e9a2018-02-01 06:11:513805 root, "window.w.location.href = '" + isolated_url.spec() + "';"));
Fergal Daly83bc3cd2023-01-18 00:22:543806 ASSERT_TRUE(manager.WaitForNavigationFinished());
Alex Moshchuk46a28e9a2018-02-01 06:11:513807 }
3808
3809 // Simulate the isolated origin in the popup navigating to bar.com.
3810 GURL bar_url(embedded_test_server()->GetURL("bar.com", "/title2.html"));
3811 {
3812 TestNavigationManager manager(new_shell->web_contents(), bar_url);
Avi Drissmanc91bd8e2021-04-19 23:58:443813 EXPECT_TRUE(ExecJs(new_shell, "location.href = '" + bar_url.spec() + "';"));
Fergal Daly83bc3cd2023-01-18 00:22:543814 ASSERT_TRUE(manager.WaitForNavigationFinished());
Alex Moshchuk46a28e9a2018-02-01 06:11:513815 }
3816
Aaron Colwelle953e562019-07-24 16:47:363817 const SiteInstanceImpl* const root_site_instance_impl =
3818 static_cast<SiteInstanceImpl*>(
3819 root->current_frame_host()->GetSiteInstance());
3820 const SiteInstanceImpl* const newshell_site_instance_impl =
3821 static_cast<SiteInstanceImpl*>(
Dave Tapuska327c06c92022-06-13 20:31:513822 new_shell->web_contents()->GetPrimaryMainFrame()->GetSiteInstance());
Sharon Yang1a7a63702025-05-23 01:14:553823 if (AreStrictSiteInstancesEnabled()) {
Aaron Colwelle953e562019-07-24 16:47:363824 // At this point, the popup and the opener should still be in separate
3825 // SiteInstances.
3826 EXPECT_NE(newshell_site_instance_impl, root_site_instance_impl);
Aaron Colwell5fb878042020-12-17 19:48:443827 EXPECT_FALSE(newshell_site_instance_impl->IsDefaultSiteInstance());
Aaron Colwelle953e562019-07-24 16:47:363828 EXPECT_FALSE(root_site_instance_impl->IsDefaultSiteInstance());
Sharon Yang02279222025-01-15 19:09:193829 } else {
3830 // Without full site isolation, all sites that do not require a dedicated
3831 // process all end up in the same default SiteInstance.
3832 EXPECT_EQ(newshell_site_instance_impl, root_site_instance_impl);
3833 EXPECT_TRUE(newshell_site_instance_impl->IsDefaultSiteInstance());
Aaron Colwelle953e562019-07-24 16:47:363834 }
Alex Moshchuk46a28e9a2018-02-01 06:11:513835
3836 // Simulate the isolated origin in the popup navigating to www.foo.com.
3837 {
3838 TestNavigationManager manager(new_shell->web_contents(), foo_url);
Avi Drissmanc91bd8e2021-04-19 23:58:443839 EXPECT_TRUE(ExecJs(new_shell, "location.href = '" + foo_url.spec() + "';"));
Fergal Daly83bc3cd2023-01-18 00:22:543840 ASSERT_TRUE(manager.WaitForNavigationFinished());
Alex Moshchuk46a28e9a2018-02-01 06:11:513841 }
3842
3843 // The popup should now be in the same SiteInstance as its same-site opener.
Dave Tapuska327c06c92022-06-13 20:31:513844 EXPECT_EQ(new_shell->web_contents()->GetPrimaryMainFrame()->GetSiteInstance(),
Alex Moshchuk46a28e9a2018-02-01 06:11:513845 root->current_frame_host()->GetSiteInstance());
3846
3847 // Check that the popup can script the opener.
Avi Drissmanc91bd8e2021-04-19 23:58:443848 EXPECT_EQ(foo_url.spec(), EvalJs(new_shell, "window.opener.location.href;"));
Alex Moshchuk46a28e9a2018-02-01 06:11:513849}
3850
Alex Moshchukd65e9a92017-12-27 22:19:373851// Check that with an ABA hierarchy, where B is an isolated origin, the root
3852// and grandchild frames end up in the same process and can script each other.
3853// See https://crbug.com/796912.
3854IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,
3855 IsolatedOriginSubframeCreatesGrandchildInRootSite) {
3856 // Start at foo.com and do a cross-site, renderer-initiated navigation to
3857 // bar.com, which should stay in the same SiteInstance (outside of
3858 // --site-per-process mode). This sets up the main frame such that its
3859 // SiteInstance's site URL does not match its actual origin - a prerequisite
3860 // for https://crbug.com/796912 to happen.
3861 GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
3862 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
3863 GURL bar_url(
3864 embedded_test_server()->GetURL("bar.com", "/page_with_iframe.html"));
3865 TestNavigationObserver observer(web_contents());
Avi Drissmanc91bd8e2021-04-19 23:58:443866 EXPECT_TRUE(ExecJs(shell(), "location.href = '" + bar_url.spec() + "';"));
Alex Moshchukd65e9a92017-12-27 22:19:373867 observer.Wait();
3868
Carlos Caballero15caeeb2021-10-27 09:57:553869 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Alex Moshchukd65e9a92017-12-27 22:19:373870 FrameTreeNode* child = root->child_at(0);
3871
3872 // Navigate bar.com's subframe to an isolated origin with its own subframe.
3873 GURL isolated_url(embedded_test_server()->GetURL("isolated.foo.com",
3874 "/page_with_iframe.html"));
3875 NavigateIframeToURL(web_contents(), "test_iframe", isolated_url);
3876 EXPECT_EQ(isolated_url, child->current_url());
3877 FrameTreeNode* grandchild = child->child_at(0);
3878
3879 // Navigate the isolated origin's subframe back to bar.com, completing the
3880 // ABA hierarchy.
Lukasz Anforowicz69c25dfd2020-11-12 21:50:203881 EXPECT_TRUE(NavigateToURLFromRenderer(grandchild, bar_url));
Alex Moshchukd65e9a92017-12-27 22:19:373882
3883 // The root and grandchild should be in the same SiteInstance, and the
3884 // middle child should be in a different SiteInstance.
3885 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
3886 child->current_frame_host()->GetSiteInstance());
3887 EXPECT_NE(child->current_frame_host()->GetSiteInstance(),
3888 grandchild->current_frame_host()->GetSiteInstance());
3889 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
3890 grandchild->current_frame_host()->GetSiteInstance());
3891
3892 // Check that the root frame can script the same-site grandchild frame.
Avi Drissmanc91bd8e2021-04-19 23:58:443893 EXPECT_EQ(bar_url.spec(), EvalJs(root, "frames[0][0].location.href;"));
Alex Moshchukd65e9a92017-12-27 22:19:373894}
3895
alexmos3b9ad102017-05-26 23:41:083896// Check that isolated origins can access cookies. This requires cookie checks
3897// on the IO thread to be aware of isolated origins.
3898IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, Cookies) {
3899 GURL isolated_url(
3900 embedded_test_server()->GetURL("isolated.foo.com", "/title2.html"));
3901 EXPECT_TRUE(NavigateToURL(shell(), isolated_url));
3902
Avi Drissmanc91bd8e2021-04-19 23:58:443903 EXPECT_TRUE(ExecJs(web_contents(), "document.cookie = 'foo=bar';"));
alexmos3b9ad102017-05-26 23:41:083904
Avi Drissmanc91bd8e2021-04-19 23:58:443905 EXPECT_EQ("foo=bar", EvalJs(web_contents(), "document.cookie;"));
alexmos3b9ad102017-05-26 23:41:083906}
3907
alexmos13fe1962017-06-28 04:25:123908// Check that isolated origins won't be placed into processes for other sites
3909// when over the process limit.
3910IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, ProcessLimit) {
3911 // Set the process limit to 1.
3912 RenderProcessHost::SetMaxRendererProcessCount(1);
3913
3914 // Navigate to an unisolated foo.com URL with an iframe.
3915 GURL foo_url(
3916 embedded_test_server()->GetURL("www.foo.com", "/page_with_iframe.html"));
3917 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
Carlos Caballero15caeeb2021-10-27 09:57:553918 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmos13fe1962017-06-28 04:25:123919 RenderProcessHost* foo_process = root->current_frame_host()->GetProcess();
3920 FrameTreeNode* child = root->child_at(0);
3921
3922 // Navigate iframe to an isolated origin.
3923 GURL isolated_foo_url(
3924 embedded_test_server()->GetURL("isolated.foo.com", "/title2.html"));
3925 NavigateIframeToURL(web_contents(), "test_iframe", isolated_foo_url);
3926
3927 // Ensure that the subframe was rendered in a new process.
3928 EXPECT_NE(child->current_frame_host()->GetProcess(), foo_process);
3929
3930 // Sanity-check IsSuitableHost values for the current processes.
Alex Moshchuk8e5c1952019-01-15 03:39:503931 const IsolationContext& isolation_context =
3932 root->current_frame_host()->GetSiteInstance()->GetIsolationContext();
W. James MacLeane84fa112020-07-14 17:25:543933 auto is_suitable_host = [&isolation_context](RenderProcessHost* process,
3934 const GURL& url) {
Alex Moshchuk8e5c1952019-01-15 03:39:503935 return RenderProcessHostImpl::IsSuitableHost(
W. James MacLeane84fa112020-07-14 17:25:543936 process, isolation_context,
Aaron Colwell9d0f9392021-02-11 21:51:523937 SiteInfo::CreateForTesting(isolation_context, url));
alexmos13fe1962017-06-28 04:25:123938 };
3939 EXPECT_TRUE(is_suitable_host(foo_process, foo_url));
3940 EXPECT_FALSE(is_suitable_host(foo_process, isolated_foo_url));
3941 EXPECT_TRUE(is_suitable_host(child->current_frame_host()->GetProcess(),
3942 isolated_foo_url));
3943 EXPECT_FALSE(
3944 is_suitable_host(child->current_frame_host()->GetProcess(), foo_url));
3945
3946 // Open a new, unrelated tab and navigate it to isolated.foo.com. This
3947 // should use a new, unrelated SiteInstance that reuses the existing isolated
3948 // origin process from first tab's subframe.
3949 Shell* new_shell = CreateBrowser();
3950 EXPECT_TRUE(NavigateToURL(new_shell, isolated_foo_url));
3951 scoped_refptr<SiteInstance> isolated_foo_instance(
Dave Tapuska327c06c92022-06-13 20:31:513952 new_shell->web_contents()->GetPrimaryMainFrame()->GetSiteInstance());
alexmos13fe1962017-06-28 04:25:123953 RenderProcessHost* isolated_foo_process = isolated_foo_instance->GetProcess();
3954 EXPECT_NE(child->current_frame_host()->GetSiteInstance(),
3955 isolated_foo_instance);
3956 EXPECT_FALSE(isolated_foo_instance->IsRelatedSiteInstance(
3957 child->current_frame_host()->GetSiteInstance()));
3958 // TODO(alexmos): with --site-per-process, this won't currently reuse the
3959 // subframe process, because the new SiteInstance will initialize its
3960 // process while it still has no site (during CreateBrowser()), and since
3961 // dedicated processes can't currently be reused for a SiteInstance with no
3962 // site, this creates a new process. The subsequent navigation to
Ari Chivukula5350aad92021-08-10 02:42:243963 // `isolated_foo_url` stays in that new process without consulting whether it
alexmos13fe1962017-06-28 04:25:123964 // can now reuse a different process. This should be fixed; see
3965 // https://crbug.com/513036. Without --site-per-process, this works because
3966 // the site-less SiteInstance is allowed to reuse the first tab's foo.com
3967 // process (which isn't dedicated), and then it swaps to the isolated.foo.com
3968 // process during navigation.
Jagadesh P1c8699c22023-11-13 14:09:373969 if (!AreAllSitesIsolatedForTesting()) {
alexmos13fe1962017-06-28 04:25:123970 EXPECT_EQ(child->current_frame_host()->GetProcess(), isolated_foo_process);
Jagadesh P1c8699c22023-11-13 14:09:373971 }
alexmos13fe1962017-06-28 04:25:123972
3973 // Navigate iframe on the first tab to a non-isolated site. This should swap
3974 // processes so that it does not reuse the isolated origin's process.
Alex Moshchukfadadd12017-10-05 16:00:553975 RenderFrameDeletedObserver deleted_observer(child->current_frame_host());
alexmos13fe1962017-06-28 04:25:123976 NavigateIframeToURL(
3977 web_contents(), "test_iframe",
3978 embedded_test_server()->GetURL("www.foo.com", "/title1.html"));
3979 EXPECT_EQ(foo_process, child->current_frame_host()->GetProcess());
3980 EXPECT_NE(isolated_foo_process, child->current_frame_host()->GetProcess());
Alex Moshchukfadadd12017-10-05 16:00:553981 deleted_observer.WaitUntilDeleted();
alexmos13fe1962017-06-28 04:25:123982
3983 // Navigate iframe back to isolated origin. See that it reuses the
Ari Chivukula5350aad92021-08-10 02:42:243984 // `new_shell` process.
alexmos13fe1962017-06-28 04:25:123985 NavigateIframeToURL(web_contents(), "test_iframe", isolated_foo_url);
3986 EXPECT_NE(foo_process, child->current_frame_host()->GetProcess());
3987 EXPECT_EQ(isolated_foo_process, child->current_frame_host()->GetProcess());
3988
3989 // Navigate iframe to a different isolated origin. Ensure that this creates
3990 // a third process.
3991 GURL isolated_bar_url(
3992 embedded_test_server()->GetURL("isolated.bar.com", "/title3.html"));
3993 NavigateIframeToURL(web_contents(), "test_iframe", isolated_bar_url);
3994 RenderProcessHost* isolated_bar_process =
3995 child->current_frame_host()->GetProcess();
3996 EXPECT_NE(foo_process, isolated_bar_process);
3997 EXPECT_NE(isolated_foo_process, isolated_bar_process);
3998
3999 // The new process should only be suitable to host isolated.bar.com, not
4000 // regular web URLs or other isolated origins.
4001 EXPECT_TRUE(is_suitable_host(isolated_bar_process, isolated_bar_url));
4002 EXPECT_FALSE(is_suitable_host(isolated_bar_process, foo_url));
4003 EXPECT_FALSE(is_suitable_host(isolated_bar_process, isolated_foo_url));
4004
4005 // Navigate second tab (currently at isolated.foo.com) to the
4006 // second isolated origin, and see that it switches processes.
4007 EXPECT_TRUE(NavigateToURL(new_shell, isolated_bar_url));
4008 EXPECT_NE(foo_process,
Dave Tapuska327c06c92022-06-13 20:31:514009 new_shell->web_contents()->GetPrimaryMainFrame()->GetProcess());
alexmos13fe1962017-06-28 04:25:124010 EXPECT_NE(isolated_foo_process,
Dave Tapuska327c06c92022-06-13 20:31:514011 new_shell->web_contents()->GetPrimaryMainFrame()->GetProcess());
alexmos13fe1962017-06-28 04:25:124012 EXPECT_EQ(isolated_bar_process,
Dave Tapuska327c06c92022-06-13 20:31:514013 new_shell->web_contents()->GetPrimaryMainFrame()->GetProcess());
alexmos13fe1962017-06-28 04:25:124014
4015 // Navigate second tab to a non-isolated URL and see that it goes back into
4016 // the www.foo.com process, and that it does not share processes with any
4017 // isolated origins.
4018 EXPECT_TRUE(NavigateToURL(new_shell, foo_url));
4019 EXPECT_EQ(foo_process,
Dave Tapuska327c06c92022-06-13 20:31:514020 new_shell->web_contents()->GetPrimaryMainFrame()->GetProcess());
alexmos13fe1962017-06-28 04:25:124021 EXPECT_NE(isolated_foo_process,
Dave Tapuska327c06c92022-06-13 20:31:514022 new_shell->web_contents()->GetPrimaryMainFrame()->GetProcess());
alexmos13fe1962017-06-28 04:25:124023 EXPECT_NE(isolated_bar_process,
Dave Tapuska327c06c92022-06-13 20:31:514024 new_shell->web_contents()->GetPrimaryMainFrame()->GetProcess());
alexmos13fe1962017-06-28 04:25:124025}
4026
Alex Moshchuk54bf13042017-10-13 04:25:294027// Verify that a navigation to an non-isolated origin does not reuse a process
4028// from a pending navigation to an isolated origin. See
4029// https://crbug.com/738634.
4030IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,
4031 ProcessReuseWithResponseStartedFromIsolatedOrigin) {
Alex Moshchuk54bf13042017-10-13 04:25:294032 // Set the process limit to 1.
4033 RenderProcessHost::SetMaxRendererProcessCount(1);
4034
4035 // Start, but don't commit a navigation to an unisolated foo.com URL.
4036 GURL slow_url(embedded_test_server()->GetURL("www.foo.com", "/title1.html"));
4037 NavigationController::LoadURLParams load_params(slow_url);
4038 TestNavigationManager foo_delayer(shell()->web_contents(), slow_url);
4039 shell()->web_contents()->GetController().LoadURL(
4040 slow_url, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
4041 EXPECT_TRUE(foo_delayer.WaitForRequestStart());
4042
4043 // Open a new, unrelated tab and navigate it to isolated.foo.com.
4044 Shell* new_shell = CreateBrowser();
4045 GURL isolated_url(
4046 embedded_test_server()->GetURL("isolated.foo.com", "/title2.html"));
4047 TestNavigationManager isolated_delayer(new_shell->web_contents(),
4048 isolated_url);
4049 new_shell->web_contents()->GetController().LoadURL(
4050 isolated_url, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
4051
Arthur Hemery0dd65812019-08-01 14:18:454052 // Wait for the response from the isolated origin. After this returns, we made
4053 // the final pick for the process to use for this navigation as part of
4054 // NavigationRequest::OnResponseStarted.
Alex Moshchuk54bf13042017-10-13 04:25:294055 EXPECT_TRUE(isolated_delayer.WaitForResponse());
4056
4057 // Now, proceed with the response and commit the non-isolated URL. This
4058 // should notice that the process that was picked for this navigation is not
4059 // suitable anymore, as it should have been locked to isolated.foo.com.
Fergal Daly83bc3cd2023-01-18 00:22:544060 ASSERT_TRUE(foo_delayer.WaitForNavigationFinished());
Alex Moshchuk54bf13042017-10-13 04:25:294061
4062 // Commit the isolated origin.
Fergal Daly83bc3cd2023-01-18 00:22:544063 ASSERT_TRUE(isolated_delayer.WaitForNavigationFinished());
Alex Moshchuk54bf13042017-10-13 04:25:294064
4065 // Ensure that the isolated origin did not share a process with the first
4066 // tab.
Dave Tapuska327c06c92022-06-13 20:31:514067 EXPECT_NE(web_contents()->GetPrimaryMainFrame()->GetProcess(),
4068 new_shell->web_contents()->GetPrimaryMainFrame()->GetProcess());
Alex Moshchuk54bf13042017-10-13 04:25:294069}
4070
Alex Moshchuk9e082892017-10-20 21:00:134071// When a navigation uses a siteless SiteInstance, and a second navigation
4072// commits an isolated origin which reuses the siteless SiteInstance's process
4073// before the first navigation's response is received, ensure that the first
4074// navigation can still finish properly and transfer to a new process, without
4075// an origin lock mismatch. See https://crbug.com/773809.
4076IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,
4077 ProcessReuseWithLazilyAssignedSiteInstance) {
Alex Moshchuk9e082892017-10-20 21:00:134078 // Set the process limit to 1.
4079 RenderProcessHost::SetMaxRendererProcessCount(1);
4080
4081 // Start from an about:blank page, where the SiteInstance will not have a
4082 // site assigned, but will have an associated process.
4083 EXPECT_TRUE(NavigateToURL(shell(), GURL(url::kAboutBlankURL)));
4084 SiteInstanceImpl* starting_site_instance = static_cast<SiteInstanceImpl*>(
Dave Tapuska327c06c92022-06-13 20:31:514085 shell()->web_contents()->GetPrimaryMainFrame()->GetSiteInstance());
Alex Moshchuk9e082892017-10-20 21:00:134086 EXPECT_FALSE(starting_site_instance->HasSite());
4087 EXPECT_TRUE(starting_site_instance->HasProcess());
4088
4089 // Inject and click a link to a non-isolated origin www.foo.com. Note that
4090 // setting location.href won't work here, as that goes through OpenURL
4091 // instead of OnBeginNavigation when starting from an about:blank page, and
4092 // that doesn't trigger this bug.
4093 GURL foo_url(embedded_test_server()->GetURL("www.foo.com", "/title1.html"));
4094 TestNavigationManager manager(shell()->web_contents(), foo_url);
Alex Moshchuk226d2c902017-11-02 21:43:134095 InjectAndClickLinkTo(foo_url);
Alex Moshchuk9e082892017-10-20 21:00:134096 EXPECT_TRUE(manager.WaitForRequestStart());
4097
4098 // Before response is received, open a new, unrelated tab and navigate it to
4099 // isolated.foo.com. This reuses the first process, which is still considered
4100 // unused at this point, and locks it to isolated.foo.com.
4101 Shell* new_shell = CreateBrowser();
4102 GURL isolated_url(
4103 embedded_test_server()->GetURL("isolated.foo.com", "/title2.html"));
4104 EXPECT_TRUE(NavigateToURL(new_shell, isolated_url));
Dave Tapuska327c06c92022-06-13 20:31:514105 EXPECT_EQ(web_contents()->GetPrimaryMainFrame()->GetProcess(),
4106 new_shell->web_contents()->GetPrimaryMainFrame()->GetProcess());
Alex Moshchuk9e082892017-10-20 21:00:134107
Alex Moshchuk494038ff2017-10-23 17:59:294108 // Wait for response from the first tab. This should notice that the first
4109 // process is no longer suitable for the final destination (which is an
4110 // unisolated URL) and transfer to another process. In
Alex Moshchuk9e082892017-10-20 21:00:134111 // https://crbug.com/773809, this led to a CHECK due to origin lock mismatch.
Fergal Daly83bc3cd2023-01-18 00:22:544112 ASSERT_TRUE(manager.WaitForNavigationFinished());
Alex Moshchuk9e082892017-10-20 21:00:134113
4114 // Ensure that the isolated origin did not share a process with the first
4115 // tab.
Dave Tapuska327c06c92022-06-13 20:31:514116 EXPECT_NE(web_contents()->GetPrimaryMainFrame()->GetProcess(),
4117 new_shell->web_contents()->GetPrimaryMainFrame()->GetProcess());
Alex Moshchuk9e082892017-10-20 21:00:134118}
4119
Alex Moshchuk494038ff2017-10-23 17:59:294120// Same as ProcessReuseWithLazilyAssignedSiteInstance above, but here the
4121// navigation with a siteless SiteInstance is for an isolated origin, and the
4122// unrelated tab loads an unisolated URL which reuses the siteless
4123// SiteInstance's process. Although the unisolated URL won't lock that process
4124// to an origin (except when running with --site-per-process), it should still
4125// mark it as used and cause the isolated origin to transfer when it receives a
4126// response. See https://crbug.com/773809.
4127IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,
4128 ProcessReuseWithLazilyAssignedIsolatedSiteInstance) {
Alex Moshchuk494038ff2017-10-23 17:59:294129 // Set the process limit to 1.
4130 RenderProcessHost::SetMaxRendererProcessCount(1);
4131
4132 // Start from an about:blank page, where the SiteInstance will not have a
4133 // site assigned, but will have an associated process.
4134 EXPECT_TRUE(NavigateToURL(shell(), GURL(url::kAboutBlankURL)));
4135 SiteInstanceImpl* starting_site_instance = static_cast<SiteInstanceImpl*>(
Dave Tapuska327c06c92022-06-13 20:31:514136 shell()->web_contents()->GetPrimaryMainFrame()->GetSiteInstance());
Alex Moshchuk494038ff2017-10-23 17:59:294137 EXPECT_FALSE(starting_site_instance->HasSite());
4138 EXPECT_TRUE(starting_site_instance->HasProcess());
Dave Tapuska327c06c92022-06-13 20:31:514139 EXPECT_TRUE(web_contents()->GetPrimaryMainFrame()->GetProcess()->IsUnused());
Alex Moshchuk494038ff2017-10-23 17:59:294140
4141 // Inject and click a link to an isolated origin. Note that
4142 // setting location.href won't work here, as that goes through OpenURL
4143 // instead of OnBeginNavigation when starting from an about:blank page, and
4144 // that doesn't trigger this bug.
4145 GURL isolated_url(
4146 embedded_test_server()->GetURL("isolated.foo.com", "/title2.html"));
4147 TestNavigationManager manager(shell()->web_contents(), isolated_url);
Alex Moshchuk226d2c902017-11-02 21:43:134148 InjectAndClickLinkTo(isolated_url);
Alex Moshchuk494038ff2017-10-23 17:59:294149 EXPECT_TRUE(manager.WaitForRequestStart());
4150
4151 // Before response is received, open a new, unrelated tab and navigate it to
4152 // an unisolated URL. This should reuse the first process, which is still
4153 // considered unused at this point, and marks it as used.
4154 Shell* new_shell = CreateBrowser();
4155 GURL foo_url(embedded_test_server()->GetURL("www.foo.com", "/title1.html"));
4156 EXPECT_TRUE(NavigateToURL(new_shell, foo_url));
Dave Tapuska327c06c92022-06-13 20:31:514157 EXPECT_EQ(web_contents()->GetPrimaryMainFrame()->GetProcess(),
4158 new_shell->web_contents()->GetPrimaryMainFrame()->GetProcess());
4159 EXPECT_FALSE(web_contents()->GetPrimaryMainFrame()->GetProcess()->IsUnused());
Alex Moshchuk494038ff2017-10-23 17:59:294160
4161 // Wait for response in the first tab. This should notice that the first
4162 // process is no longer suitable for the isolated origin because it should
4163 // already be marked as used, and transfer to another process.
Fergal Daly83bc3cd2023-01-18 00:22:544164 ASSERT_TRUE(manager.WaitForNavigationFinished());
Alex Moshchuk494038ff2017-10-23 17:59:294165
4166 // Ensure that the isolated origin did not share a process with the second
4167 // tab.
Dave Tapuska327c06c92022-06-13 20:31:514168 EXPECT_NE(web_contents()->GetPrimaryMainFrame()->GetProcess(),
4169 new_shell->web_contents()->GetPrimaryMainFrame()->GetProcess());
Alex Moshchuk494038ff2017-10-23 17:59:294170}
4171
Alex Moshchuk54bf13042017-10-13 04:25:294172// Verify that a navigation to an unisolated origin cannot reuse a process from
4173// a pending navigation to an isolated origin. Similar to
4174// ProcessReuseWithResponseStartedFromIsolatedOrigin, but here the non-isolated
4175// URL is the first to reach OnResponseStarted, which should mark the process
4176// as "used", so that the isolated origin can't reuse it. See
4177// https://crbug.com/738634.
4178IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,
4179 ProcessReuseWithResponseStartedFromUnisolatedOrigin) {
Alex Moshchuk54bf13042017-10-13 04:25:294180 // Set the process limit to 1.
4181 RenderProcessHost::SetMaxRendererProcessCount(1);
4182
4183 // Start a navigation to an unisolated foo.com URL.
4184 GURL slow_url(embedded_test_server()->GetURL("www.foo.com", "/title1.html"));
4185 NavigationController::LoadURLParams load_params(slow_url);
4186 TestNavigationManager foo_delayer(shell()->web_contents(), slow_url);
4187 shell()->web_contents()->GetController().LoadURL(
4188 slow_url, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
4189
Arthur Hemery0dd65812019-08-01 14:18:454190 // Wait for the response for foo.com. After this returns, we should have made
4191 // the final pick for the process to use for foo.com, so this should mark the
4192 // process as "used" and ineligible for reuse by isolated.foo.com below.
Alex Moshchuk54bf13042017-10-13 04:25:294193 EXPECT_TRUE(foo_delayer.WaitForResponse());
4194
4195 // Open a new, unrelated tab, navigate it to isolated.foo.com, and wait for
4196 // the navigation to fully load.
4197 Shell* new_shell = CreateBrowser();
4198 GURL isolated_url(
4199 embedded_test_server()->GetURL("isolated.foo.com", "/title2.html"));
4200 EXPECT_TRUE(NavigateToURL(new_shell, isolated_url));
4201
4202 // Finish loading the foo.com URL.
Fergal Daly83bc3cd2023-01-18 00:22:544203 ASSERT_TRUE(foo_delayer.WaitForNavigationFinished());
Alex Moshchuk54bf13042017-10-13 04:25:294204
4205 // Ensure that the isolated origin did not share a process with the first
4206 // tab.
Dave Tapuska327c06c92022-06-13 20:31:514207 EXPECT_NE(web_contents()->GetPrimaryMainFrame()->GetProcess(),
4208 new_shell->web_contents()->GetPrimaryMainFrame()->GetProcess());
Alex Moshchuk54bf13042017-10-13 04:25:294209}
4210
Alex Moshchuk226d2c902017-11-02 21:43:134211// Verify that when a process has a pending SiteProcessCountTracker entry for
4212// an isolated origin, and a navigation to a non-isolated origin reuses that
4213// process, future isolated origin subframe navigations do not reuse that
4214// process. See https://crbug.com/780661.
4215IN_PROC_BROWSER_TEST_F(
4216 IsolatedOriginTest,
4217 IsolatedSubframeDoesNotReuseUnsuitableProcessWithPendingSiteEntry) {
Alex Moshchuk226d2c902017-11-02 21:43:134218 // Set the process limit to 1.
4219 RenderProcessHost::SetMaxRendererProcessCount(1);
4220
4221 // Start from an about:blank page, where the SiteInstance will not have a
4222 // site assigned, but will have an associated process.
4223 EXPECT_TRUE(NavigateToURL(shell(), GURL(url::kAboutBlankURL)));
Dave Tapuska327c06c92022-06-13 20:31:514224 EXPECT_TRUE(web_contents()->GetPrimaryMainFrame()->GetProcess()->IsUnused());
Alex Moshchuk226d2c902017-11-02 21:43:134225
4226 // Inject and click a link to an isolated origin URL which never sends back a
4227 // response.
4228 GURL hung_isolated_url(
4229 embedded_test_server()->GetURL("isolated.foo.com", "/hung"));
4230 TestNavigationManager manager(web_contents(), hung_isolated_url);
4231 InjectAndClickLinkTo(hung_isolated_url);
4232
4233 // Wait for the request and send it. This will place
4234 // isolated.foo.com on the list of pending sites for this tab's process.
4235 EXPECT_TRUE(manager.WaitForRequestStart());
4236 manager.ResumeNavigation();
4237
4238 // Open a new, unrelated tab and navigate it to an unisolated URL. This
4239 // should reuse the first process, which is still considered unused at this
4240 // point, and mark it as used.
4241 Shell* new_shell = CreateBrowser();
4242 GURL foo_url(
4243 embedded_test_server()->GetURL("www.foo.com", "/page_with_iframe.html"));
4244 EXPECT_TRUE(NavigateToURL(new_shell, foo_url));
4245
4246 // Navigate iframe on second tab to isolated.foo.com. This should *not*
4247 // reuse the first process, even though isolated.foo.com is still in its list
4248 // of pending sites (from the hung navigation in the first tab). That
4249 // process is unsuitable because it now contains www.foo.com.
4250 GURL isolated_url(
4251 embedded_test_server()->GetURL("isolated.foo.com", "/title1.html"));
4252 NavigateIframeToURL(new_shell->web_contents(), "test_iframe", isolated_url);
4253
4254 FrameTreeNode* root = static_cast<WebContentsImpl*>(new_shell->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:554255 ->GetPrimaryFrameTree()
4256 .root();
Alex Moshchuk226d2c902017-11-02 21:43:134257 FrameTreeNode* child = root->child_at(0);
4258 EXPECT_NE(child->current_frame_host()->GetProcess(),
4259 root->current_frame_host()->GetProcess());
4260
4261 // Manipulating cookies from the main frame should not result in a renderer
4262 // kill.
Avi Drissmanc91bd8e2021-04-19 23:58:444263 EXPECT_TRUE(
4264 ExecJs(root->current_frame_host(), "document.cookie = 'foo=bar';"));
4265 EXPECT_EQ("foo=bar", EvalJs(root->current_frame_host(), "document.cookie;"));
Alex Moshchuk226d2c902017-11-02 21:43:134266}
4267
4268// Similar to the test above, but for a ServiceWorker. When a process has a
4269// pending SiteProcessCountTracker entry for an isolated origin, and a
4270// navigation to a non-isolated origin reuses that process, a ServiceWorker
4271// subsequently created for that isolated origin shouldn't reuse that process.
4272// See https://crbug.com/780661 and https://crbug.com/780089.
4273IN_PROC_BROWSER_TEST_F(
4274 IsolatedOriginTest,
4275 IsolatedServiceWorkerDoesNotReuseUnsuitableProcessWithPendingSiteEntry) {
Alex Moshchuk226d2c902017-11-02 21:43:134276 // Set the process limit to 1.
4277 RenderProcessHost::SetMaxRendererProcessCount(1);
4278
4279 // Start from an about:blank page, where the SiteInstance will not have a
4280 // site assigned, but will have an associated process.
4281 EXPECT_TRUE(NavigateToURL(shell(), GURL(url::kAboutBlankURL)));
Dave Tapuska327c06c92022-06-13 20:31:514282 EXPECT_TRUE(web_contents()->GetPrimaryMainFrame()->GetProcess()->IsUnused());
Alex Moshchuk226d2c902017-11-02 21:43:134283
4284 // Inject and click a link to an isolated origin URL which never sends back a
4285 // response.
4286 GURL hung_isolated_url(
4287 embedded_test_server()->GetURL("isolated.foo.com", "/hung"));
4288 TestNavigationManager manager(shell()->web_contents(), hung_isolated_url);
4289 InjectAndClickLinkTo(hung_isolated_url);
4290
4291 // Wait for the request and send it. This will place
4292 // isolated.foo.com on the list of pending sites for this tab's process.
Alex Moshchuk226d2c902017-11-02 21:43:134293 EXPECT_TRUE(manager.WaitForRequestStart());
4294 manager.ResumeNavigation();
4295
Jiacheng Guo15d61372025-08-13 06:11:254296 // Wait for the navigation to take the RFH and RPH of the blank page before
4297 // triggering the second request. Since the navigation request may reuse the
4298 // RFH of the blank page rather than creating a speculative RFH here, we
4299 // cannot use manager.WaitForSpeculativeRFHCreation(). Otherwise the second
4300 // navigation may take the RPH of the blank page, causing the service worker
4301 // to be matched.
4302 auto* request =
4303 web_contents()->GetPrimaryFrameTree().root()->navigation_request();
4304 EXPECT_TRUE(base::test::RunUntil([&]() {
4305 return request->GetAssociatedRFHType() !=
4306 NavigationRequest::AssociatedRenderFrameHostType::NONE;
4307 }));
4308
Alex Moshchuk226d2c902017-11-02 21:43:134309 // Open a new, unrelated tab and navigate it to an unisolated URL. This
4310 // should reuse the first process, which is still considered unused at this
4311 // point, and mark it as used.
4312 Shell* new_shell = CreateBrowser();
4313 GURL foo_url(embedded_test_server()->GetURL("www.foo.com", "/title1.html"));
4314 EXPECT_TRUE(NavigateToURL(new_shell, foo_url));
4315
4316 // A SiteInstance created for an isolated origin ServiceWorker should
4317 // not reuse the unsuitable first process.
Robbie McElrath6fd8d5cc2021-08-04 05:42:334318 BrowserContext* browser_context = web_contents()->GetBrowserContext();
Alex Moshchuk226d2c902017-11-02 21:43:134319 scoped_refptr<SiteInstanceImpl> sw_site_instance =
Aaron Colwellecc72fb2019-04-30 17:41:114320 SiteInstanceImpl::CreateForServiceWorker(
Robbie McElrath6fd8d5cc2021-08-04 05:42:334321 browser_context,
4322 UrlInfo::CreateForTesting(
4323 hung_isolated_url,
4324 StoragePartitionConfig::CreateDefault(browser_context)),
Aaron Colwellfaa736e2019-12-10 02:24:484325 /* can_reuse_process= */ true);
Jiacheng Guocd621762025-06-17 01:14:324326 RenderProcessHost* sw_host = sw_site_instance->GetOrCreateProcessForTesting();
Dave Tapuska327c06c92022-06-13 20:31:514327 EXPECT_NE(new_shell->web_contents()->GetPrimaryMainFrame()->GetProcess(),
4328 sw_host);
Alex Moshchuk226d2c902017-11-02 21:43:134329
4330 // Cancel the hung request and commit a real navigation to an isolated
4331 // origin. This should now end up in the ServiceWorker's process.
Daniel Cheng390e2a72022-09-28 06:07:534332 web_contents()->GetPrimaryFrameTree().root()->ResetNavigationRequest(
Rakina Zata Amni58681c62024-06-25 06:32:134333 NavigationDiscardReason::kExplicitCancellation);
Alex Moshchuk226d2c902017-11-02 21:43:134334 GURL isolated_url(
4335 embedded_test_server()->GetURL("isolated.foo.com", "/title1.html"));
4336 EXPECT_TRUE(NavigateToURL(shell(), isolated_url));
Dave Tapuska327c06c92022-06-13 20:31:514337 EXPECT_EQ(web_contents()->GetPrimaryMainFrame()->GetProcess(), sw_host);
Alex Moshchuk226d2c902017-11-02 21:43:134338}
4339
alexmos4bc26322017-07-01 00:57:144340// Check that subdomains on an isolated origin (e.g., bar.isolated.foo.com)
4341// also end up in the isolated origin's SiteInstance.
4342IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, IsolatedOriginWithSubdomain) {
4343 // Start on a page with an isolated origin with a same-site iframe.
4344 GURL isolated_url(embedded_test_server()->GetURL("isolated.foo.com",
4345 "/page_with_iframe.html"));
4346 EXPECT_TRUE(NavigateToURL(shell(), isolated_url));
4347
Carlos Caballero15caeeb2021-10-27 09:57:554348 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmos4bc26322017-07-01 00:57:144349 FrameTreeNode* child = root->child_at(0);
4350 scoped_refptr<SiteInstance> isolated_instance =
4351 web_contents()->GetSiteInstance();
4352
4353 // Navigate iframe to the isolated origin's subdomain.
4354 GURL isolated_subdomain_url(
4355 embedded_test_server()->GetURL("bar.isolated.foo.com", "/title1.html"));
4356 NavigateIframeToURL(web_contents(), "test_iframe", isolated_subdomain_url);
4357 EXPECT_EQ(child->current_url(), isolated_subdomain_url);
4358
4359 EXPECT_EQ(isolated_instance, child->current_frame_host()->GetSiteInstance());
4360 EXPECT_FALSE(child->current_frame_host()->IsCrossProcessSubframe());
Lukasz Anforowicz25420932018-12-18 20:59:224361 EXPECT_EQ(GURL("http://isolated.foo.com/"),
alexmos4bc26322017-07-01 00:57:144362 child->current_frame_host()->GetSiteInstance()->GetSiteURL());
4363
4364 // Now try navigating the main frame (renderer-initiated) to the isolated
4365 // origin's subdomain. This should not swap processes.
4366 TestNavigationObserver observer(web_contents());
Avi Drissmanc91bd8e2021-04-19 23:58:444367 EXPECT_TRUE(ExecJs(web_contents(), "location.href = '" +
4368 isolated_subdomain_url.spec() + "'"));
alexmos4bc26322017-07-01 00:57:144369 observer.Wait();
Rakina Zata Amnid3af5db92020-08-06 06:51:394370 if (CanSameSiteMainFrameNavigationsChangeSiteInstances()) {
4371 // If same-site ProactivelySwapBrowsingInstance is enabled, they should be
4372 // in different site instances but in the same process.
4373 EXPECT_NE(isolated_instance, web_contents()->GetSiteInstance());
4374 EXPECT_EQ(isolated_instance->GetProcess(),
4375 web_contents()->GetSiteInstance()->GetProcess());
4376 } else {
4377 EXPECT_EQ(isolated_instance, web_contents()->GetSiteInstance());
4378 }
alexmos4bc26322017-07-01 00:57:144379}
4380
Ari Chivukulac05d6e02021-09-21 00:16:264381// This class allows intercepting the BindStorageArea and OpenLocalStorage
4382// methods in order to test what happens when parameters are changed.
Chase Phillips2c5603ad2019-03-22 17:17:584383class StoragePartitonInterceptor
Ken Rockot8b8424552020-02-20 06:12:414384 : public blink::mojom::DomStorageInterceptorForTesting,
Nasko Oskovff268562017-09-01 20:01:404385 public RenderProcessHostObserver {
Nasko Oskov59562ccf2017-08-25 03:40:004386 public:
Chase Phillips2c5603ad2019-03-22 17:17:584387 StoragePartitonInterceptor(
Marijn Kruisselbrink4712c332018-06-13 19:06:594388 RenderProcessHostImpl* rph,
Ken Rockot8b8424552020-02-20 06:12:414389 mojo::PendingReceiver<blink::mojom::DomStorage> receiver,
Arthur Sonzognic686e8f2024-01-11 08:36:374390 std::optional<blink::StorageKey> storage_key_to_inject,
4391 std::optional<blink::LocalFrameToken> local_frame_token_to_inject,
Ari Chivukulaccb16aeb2021-10-01 01:47:124392 bool inject_first_local_frame_token)
4393 : storage_key_to_inject_(storage_key_to_inject),
4394 local_frame_token_to_inject_(local_frame_token_to_inject),
4395 save_first_local_frame_token_(inject_first_local_frame_token) {
Nasko Oskovff268562017-09-01 20:01:404396 StoragePartitionImpl* storage_partition =
4397 static_cast<StoragePartitionImpl*>(rph->GetStoragePartition());
Nasko Oskov59562ccf2017-08-25 03:40:004398
Ken Rockot8b8424552020-02-20 06:12:414399 // Bind the real DomStorage implementation.
4400 mojo::PendingRemote<blink::mojom::DomStorageClient> unused_client;
Avi Drissman5d5d48d62022-01-07 20:23:584401 std::ignore = unused_client.InitWithNewPipeAndPassReceiver();
Ken Rockot8b8424552020-02-20 06:12:414402 mojo::ReceiverId receiver_id = storage_partition->BindDomStorage(
Emily Andrewsd15fd762024-12-10 20:41:544403 rph->GetDeprecatedID(), std::move(receiver), std::move(unused_client));
Nasko Oskovff268562017-09-01 20:01:404404
4405 // Now replace it with this object and keep a pointer to the real
4406 // implementation.
Ken Rockot8b8424552020-02-20 06:12:414407 dom_storage_ = storage_partition->dom_storage_receivers_for_testing()
4408 .SwapImplForTesting(receiver_id, this);
Nasko Oskovff268562017-09-01 20:01:404409
Ari Chivukula5350aad92021-08-10 02:42:244410 // Register the `this` as a RenderProcessHostObserver, so it can be
Nasko Oskovff268562017-09-01 20:01:404411 // correctly cleaned up when the process exits.
4412 rph->AddObserver(this);
4413 }
4414
Sharon Yang034fbb722021-06-23 17:21:324415 StoragePartitonInterceptor(const StoragePartitonInterceptor&) = delete;
4416 StoragePartitonInterceptor& operator=(const StoragePartitonInterceptor&) =
4417 delete;
4418
Nasko Oskovff268562017-09-01 20:01:404419 // Ensure this object is cleaned up when the process goes away, since it
4420 // is not owned by anyone else.
4421 void RenderProcessExited(RenderProcessHost* host,
Bo Liu2a489402018-04-24 23:41:274422 const ChildProcessTerminationInfo& info) override {
Nasko Oskovff268562017-09-01 20:01:404423 host->RemoveObserver(this);
4424 delete this;
Nasko Oskov59562ccf2017-08-25 03:40:004425 }
4426
Lukasz Anforowicz4cc4be82019-01-16 23:23:204427 // Allow all methods that aren't explicitly overridden to pass through
Nasko Oskov59562ccf2017-08-25 03:40:004428 // unmodified.
Ken Rockot8b8424552020-02-20 06:12:414429 blink::mojom::DomStorage* GetForwardingInterface() override {
4430 return dom_storage_;
Nasko Oskov59562ccf2017-08-25 03:40:004431 }
4432
Ari Chivukulaccb16aeb2021-10-01 01:47:124433 // Override this method to allow changing the `storage_key` or
4434 // `local_frame_token`. It simulates a renderer process sending incorrect
4435 // data to the browser process, so security checks can be tested.
Mario Sanchez Prada2590ec6d2019-08-14 17:17:054436 void OpenLocalStorage(
Ari Chivukulab54d9042021-09-21 00:27:144437 const blink::StorageKey& storage_key,
Ari Chivukulaccb16aeb2021-10-01 01:47:124438 const blink::LocalFrameToken& local_frame_token,
Ken Rockot56bf9c42019-11-16 00:00:224439 mojo::PendingReceiver<blink::mojom::StorageArea> receiver) override {
Jagadesh P1c8699c22023-11-13 14:09:374440 if (save_first_local_frame_token_ && !saved_first_local_frame_token_) {
Ari Chivukulaccb16aeb2021-10-01 01:47:124441 saved_first_local_frame_token_ = local_frame_token;
Jagadesh P1c8699c22023-11-13 14:09:374442 }
4443 if (saved_first_local_frame_token_ && !local_frame_token_to_inject_) {
Ari Chivukulaccb16aeb2021-10-01 01:47:124444 local_frame_token_to_inject_ = saved_first_local_frame_token_;
Jagadesh P1c8699c22023-11-13 14:09:374445 }
Ari Chivukulaccb16aeb2021-10-01 01:47:124446 GetForwardingInterface()->OpenLocalStorage(
4447 storage_key_to_inject_ ? *storage_key_to_inject_ : storage_key,
4448 local_frame_token_to_inject_ ? *local_frame_token_to_inject_
4449 : local_frame_token,
4450 std::move(receiver));
Nasko Oskov59562ccf2017-08-25 03:40:004451 }
4452
Ari Chivukulab54d9042021-09-21 00:27:144453 // Override this method to allow changing the `storage_key`. It simulates a
Ari Chivukulac05d6e02021-09-21 00:16:264454 // renderer process sending incorrect data to the browser process, so
4455 // security checks can be tested.
4456 void BindSessionStorageArea(
Ari Chivukulab54d9042021-09-21 00:27:144457 const blink::StorageKey& storage_key,
Ari Chivukulaccb16aeb2021-10-01 01:47:124458 const blink::LocalFrameToken& local_frame_token,
Ari Chivukulac05d6e02021-09-21 00:16:264459 const std::string& namespace_id,
4460 mojo::PendingReceiver<blink::mojom::StorageArea> receiver) override {
Jagadesh P1c8699c22023-11-13 14:09:374461 if (save_first_local_frame_token_ && !saved_first_local_frame_token_) {
Ari Chivukulaccb16aeb2021-10-01 01:47:124462 saved_first_local_frame_token_ = local_frame_token;
Jagadesh P1c8699c22023-11-13 14:09:374463 }
4464 if (saved_first_local_frame_token_ && !local_frame_token_to_inject_) {
Ari Chivukulaccb16aeb2021-10-01 01:47:124465 local_frame_token_to_inject_ = saved_first_local_frame_token_;
Jagadesh P1c8699c22023-11-13 14:09:374466 }
Ari Chivukulac05d6e02021-09-21 00:16:264467 GetForwardingInterface()->BindSessionStorageArea(
Ari Chivukulaccb16aeb2021-10-01 01:47:124468 storage_key_to_inject_ ? *storage_key_to_inject_ : storage_key,
4469 local_frame_token_to_inject_ ? *local_frame_token_to_inject_
4470 : local_frame_token,
4471 namespace_id, std::move(receiver));
Ari Chivukulac05d6e02021-09-21 00:16:264472 }
4473
Nasko Oskov59562ccf2017-08-25 03:40:004474 private:
Arthur Sonzognic686e8f2024-01-11 08:36:374475 static std::optional<blink::LocalFrameToken> saved_first_local_frame_token_;
Nasko Oskov59562ccf2017-08-25 03:40:004476 // Keep a pointer to the original implementation of the service, so all
4477 // calls can be forwarded to it.
Jagadesh P1c8699c22023-11-13 14:09:374478 raw_ptr<blink::mojom::DomStorage> dom_storage_ = nullptr;
Arthur Sonzognic686e8f2024-01-11 08:36:374479 std::optional<blink::StorageKey> storage_key_to_inject_;
4480 std::optional<blink::LocalFrameToken> local_frame_token_to_inject_;
Ari Chivukulaccb16aeb2021-10-01 01:47:124481 bool save_first_local_frame_token_;
Nasko Oskov59562ccf2017-08-25 03:40:004482};
4483
Arthur Sonzognic686e8f2024-01-11 08:36:374484std::optional<blink::LocalFrameToken>
4485 StoragePartitonInterceptor::saved_first_local_frame_token_ = std::nullopt;
Ari Chivukulaccb16aeb2021-10-01 01:47:124486
4487// Save the first LocalFrameToken seen and inject it into future calls.
4488void CreateTestDomStorageBackendToSaveFirstFrame(
4489 RenderProcessHostImpl* rph,
4490 mojo::PendingReceiver<blink::mojom::DomStorage> receiver) {
4491 // This object will register as RenderProcessHostObserver, so it will
4492 // clean itself automatically on process exit.
Arthur Sonzognic686e8f2024-01-11 08:36:374493 new StoragePartitonInterceptor(rph, std::move(receiver), std::nullopt,
4494 std::nullopt,
Ari Chivukulaccb16aeb2021-10-01 01:47:124495 /* save_first_local_frame_token_ */ true);
4496}
4497
4498// Inject (or not if null) a StorageKey and LocalFrameToken.
4499void CreateTestDomStorageBackendToInjectValues(
Arthur Sonzognic686e8f2024-01-11 08:36:374500 std::optional<blink::StorageKey> storage_key_to_inject,
4501 std::optional<blink::LocalFrameToken> local_frame_token_to_inject,
Nasko Oskov59562ccf2017-08-25 03:40:004502 RenderProcessHostImpl* rph,
Ken Rockot8b8424552020-02-20 06:12:414503 mojo::PendingReceiver<blink::mojom::DomStorage> receiver) {
Nasko Oskovff268562017-09-01 20:01:404504 // This object will register as RenderProcessHostObserver, so it will
4505 // clean itself automatically on process exit.
Ari Chivukulab54d9042021-09-21 00:27:144506 new StoragePartitonInterceptor(rph, std::move(receiver),
Ari Chivukulaccb16aeb2021-10-01 01:47:124507 storage_key_to_inject,
4508 local_frame_token_to_inject,
4509 /* save_first_local_frame_token_ */ false);
Nasko Oskov59562ccf2017-08-25 03:40:004510}
4511
Ari Chivukulac05d6e02021-09-21 00:16:264512// Verify that a renderer process cannot read sessionStorage of another origin.
Ari Chivukulaccb16aeb2021-10-01 01:47:124513IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, SessionStorage_WrongOrigin) {
Ari Chivukulab54d9042021-09-21 00:27:144514 auto mismatched_storage_key =
4515 blink::StorageKey::CreateFromStringForTesting("http://bar.com");
Ari Chivukulaccb16aeb2021-10-01 01:47:124516 RenderProcessHostImpl::SetDomStorageBinderForTesting(
4517 base::BindRepeating(&CreateTestDomStorageBackendToInjectValues,
Arthur Sonzognic686e8f2024-01-11 08:36:374518 mismatched_storage_key, std::nullopt));
Ari Chivukulac05d6e02021-09-21 00:16:264519
4520 GURL isolated_url(
4521 embedded_test_server()->GetURL("isolated.foo.com", "/title1.html"));
4522 EXPECT_TRUE(IsIsolatedOrigin(url::Origin::Create(isolated_url)));
4523 EXPECT_TRUE(NavigateToURL(shell(), isolated_url));
4524
4525 content::RenderProcessHostBadIpcMessageWaiter kill_waiter(
Dave Tapuska327c06c92022-06-13 20:31:514526 web_contents()->GetPrimaryMainFrame()->GetProcess());
Avi Drissman5d5d48d62022-01-07 20:23:584527 // Use std::ignore here, since on Android the renderer process is
Chris Fredricksond3bb2682023-05-10 03:32:264528 // terminated, but ExecJs still returns true. It properly returns
Ari Chivukulac05d6e02021-09-21 00:16:264529 // false on all other platforms.
Avi Drissman5d5d48d62022-01-07 20:23:584530 std::ignore =
Dave Tapuska327c06c92022-06-13 20:31:514531 ExecJs(web_contents()->GetPrimaryMainFrame(), "sessionStorage.length;");
Ari Chivukulac05d6e02021-09-21 00:16:264532 EXPECT_EQ(bad_message::RPH_MOJO_PROCESS_ERROR, kill_waiter.Wait());
4533}
4534
Ari Chivukulaccb16aeb2021-10-01 01:47:124535// Verify not fatal if the renderer reads sessionStorage from an empty
4536// LocalFrameToken.
4537IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,
4538 SessionStorage_EmptyLocalFrameToken) {
4539 // This sets up some initial sessionStorage state for the subsequent test.
4540 GURL page_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
4541 EXPECT_TRUE(NavigateToURL(shell(), page_url));
Dave Tapuska327c06c92022-06-13 20:31:514542 EXPECT_TRUE(ExecJs(web_contents()->GetPrimaryMainFrame(),
Ari Chivukulaccb16aeb2021-10-01 01:47:124543 "sessionStorage.setItem('key', 'value');"));
Dave Tapuska327c06c92022-06-13 20:31:514544 EXPECT_EQ(1, EvalJs(web_contents()->GetPrimaryMainFrame(),
4545 "sessionStorage.length"));
Ari Chivukulaccb16aeb2021-10-01 01:47:124546
4547 // Set up the IPC injection and crash the renderer process so that it's used.
4548 // Without crashing the renderer, the default IPC will be used.
4549 RenderProcessHostImpl::SetDomStorageBinderForTesting(
4550 base::BindRepeating(&CreateTestDomStorageBackendToInjectValues,
Arthur Sonzognic686e8f2024-01-11 08:36:374551 std::nullopt, blink::LocalFrameToken()));
Ari Chivukulaccb16aeb2021-10-01 01:47:124552 RenderProcessHost* renderer_process =
Dave Tapuska327c06c92022-06-13 20:31:514553 web_contents()->GetPrimaryMainFrame()->GetProcess();
Ari Chivukulaccb16aeb2021-10-01 01:47:124554 RenderProcessHostWatcher crash_observer(
4555 renderer_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
4556 renderer_process->Shutdown(0);
4557 crash_observer.Wait();
4558
4559 // Re-do tests now that injection is in place
4560 EXPECT_TRUE(NavigateToURL(shell(), page_url));
Dave Tapuska327c06c92022-06-13 20:31:514561 EXPECT_EQ(0, EvalJs(web_contents()->GetPrimaryMainFrame(),
4562 "sessionStorage.length"));
Ari Chivukulaccb16aeb2021-10-01 01:47:124563}
4564
Ari Chivukula15249fc2021-10-01 22:02:524565// Verify fatal error if the renderer reads sessionStorage from the wrong
Ari Chivukulaccb16aeb2021-10-01 01:47:124566// LocalFrameToken.
4567IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,
4568 SessionStorage_WrongLocalFrameToken) {
4569 // This sets up some initial sessionStorage state for the subsequent test.
4570 GURL isolated_url(embedded_test_server()->GetURL(
4571 "isolated.foo.com",
4572 "/cross_site_iframe_factory.html?isolated.foo.com(bar.com)"));
4573 EXPECT_TRUE(NavigateToURL(shell(), isolated_url));
Dave Tapuska327c06c92022-06-13 20:31:514574 EXPECT_TRUE(ExecJs(web_contents()->GetPrimaryMainFrame(),
Ari Chivukulaccb16aeb2021-10-01 01:47:124575 "sessionStorage.setItem('key', 'value');"));
Dave Tapuska327c06c92022-06-13 20:31:514576 EXPECT_EQ(1, EvalJs(web_contents()->GetPrimaryMainFrame(),
4577 "sessionStorage.length"));
Ari Chivukulaccb16aeb2021-10-01 01:47:124578 EXPECT_TRUE(ExecJs(ChildFrameAt(shell(), 0),
4579 "sessionStorage.setItem('key', 'value');"));
4580 EXPECT_EQ(1, EvalJs(ChildFrameAt(shell(), 0), "sessionStorage.length"));
4581
4582 // Set up the IPC injection and crash the renderer process so that it's used.
4583 // Without crashing the renderer, the default IPC will be used.
4584 RenderProcessHostImpl::SetDomStorageBinderForTesting(
4585 base::BindRepeating(&CreateTestDomStorageBackendToSaveFirstFrame));
4586 RenderProcessHost* renderer_process_iframe =
4587 ChildFrameAt(shell(), 0)->GetProcess();
4588 RenderProcessHostWatcher crash_observer_iframe(
4589 renderer_process_iframe,
4590 RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
4591 renderer_process_iframe->Shutdown(0);
4592 crash_observer_iframe.Wait();
4593 RenderProcessHost* renderer_process_root =
Dave Tapuska327c06c92022-06-13 20:31:514594 web_contents()->GetPrimaryMainFrame()->GetProcess();
Ari Chivukulaccb16aeb2021-10-01 01:47:124595 RenderProcessHostWatcher crash_observer_root(
4596 renderer_process_root, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
4597 renderer_process_root->Shutdown(0);
4598 crash_observer_root.Wait();
4599
4600 // Re-do tests now that injection is in place
4601 EXPECT_TRUE(NavigateToURL(shell(), isolated_url));
Dave Tapuska327c06c92022-06-13 20:31:514602 EXPECT_EQ(1, EvalJs(web_contents()->GetPrimaryMainFrame(),
4603 "sessionStorage.length"));
Ari Chivukula15249fc2021-10-01 22:02:524604 content::RenderProcessHostBadIpcMessageWaiter kill_waiter(
4605 ChildFrameAt(shell(), 0)->GetProcess());
Avi Drissman5d5d48d62022-01-07 20:23:584606 std::ignore = ExecJs(ChildFrameAt(shell(), 0), "sessionStorage.length");
Ari Chivukula15249fc2021-10-01 22:02:524607 EXPECT_EQ(bad_message::RPH_MOJO_PROCESS_ERROR, kill_waiter.Wait());
4608 // The subframe has crashed, but the main frame should still be alive and
4609 // working.
Dave Tapuska327c06c92022-06-13 20:31:514610 EXPECT_EQ(1, EvalJs(web_contents()->GetPrimaryMainFrame(),
4611 "sessionStorage.length"));
Ari Chivukulaccb16aeb2021-10-01 01:47:124612}
4613
4614// Verify not fatal if the renderer reads localStorage from an empty
4615// LocalFrameToken.
4616IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, LocalStorage_EmptyLocalFrameToken) {
4617 // This sets up some initial localStorage state for the subsequent test.
4618 GURL page_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
4619 EXPECT_TRUE(NavigateToURL(shell(), page_url));
Dave Tapuska327c06c92022-06-13 20:31:514620 EXPECT_TRUE(ExecJs(web_contents()->GetPrimaryMainFrame(),
Ari Chivukulaccb16aeb2021-10-01 01:47:124621 "localStorage.setItem('key', 'value');"));
Dave Tapuska327c06c92022-06-13 20:31:514622 EXPECT_EQ(
4623 1, EvalJs(web_contents()->GetPrimaryMainFrame(), "localStorage.length"));
Ari Chivukulaccb16aeb2021-10-01 01:47:124624
4625 // Set up the IPC injection and crash the renderer process so that it's used.
4626 // Without crashing the renderer, the default IPC will be used.
4627 RenderProcessHostImpl::SetDomStorageBinderForTesting(
4628 base::BindRepeating(&CreateTestDomStorageBackendToInjectValues,
Arthur Sonzognic686e8f2024-01-11 08:36:374629 std::nullopt, blink::LocalFrameToken()));
Ari Chivukulaccb16aeb2021-10-01 01:47:124630 RenderProcessHost* renderer_process =
Dave Tapuska327c06c92022-06-13 20:31:514631 web_contents()->GetPrimaryMainFrame()->GetProcess();
Ari Chivukulaccb16aeb2021-10-01 01:47:124632 RenderProcessHostWatcher crash_observer(
4633 renderer_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
4634 renderer_process->Shutdown(0);
4635 crash_observer.Wait();
4636
4637 // Re-do tests now that injection is in place
4638 EXPECT_TRUE(NavigateToURL(shell(), page_url));
Dave Tapuska327c06c92022-06-13 20:31:514639 EXPECT_EQ(
4640 0, EvalJs(web_contents()->GetPrimaryMainFrame(), "localStorage.length"));
Ari Chivukulaccb16aeb2021-10-01 01:47:124641}
4642
Ari Chivukula15249fc2021-10-01 22:02:524643// Verify fatal error if the renderer reads localStorage from the wrong
Ari Chivukulaccb16aeb2021-10-01 01:47:124644// LocalFrameToken.
4645IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, LocalStorage_WrongLocalFrameToken) {
4646 // This sets up some initial localStorage state for the subsequent test.
4647 GURL isolated_url(embedded_test_server()->GetURL(
4648 "isolated.foo.com",
4649 "/cross_site_iframe_factory.html?isolated.foo.com(bar.com)"));
4650 EXPECT_TRUE(NavigateToURL(shell(), isolated_url));
Dave Tapuska327c06c92022-06-13 20:31:514651 EXPECT_TRUE(ExecJs(web_contents()->GetPrimaryMainFrame(),
Ari Chivukulaccb16aeb2021-10-01 01:47:124652 "localStorage.setItem('key', 'value');"));
Dave Tapuska327c06c92022-06-13 20:31:514653 EXPECT_EQ(
4654 1, EvalJs(web_contents()->GetPrimaryMainFrame(), "localStorage.length"));
Ari Chivukulaccb16aeb2021-10-01 01:47:124655 EXPECT_TRUE(ExecJs(ChildFrameAt(shell(), 0),
4656 "localStorage.setItem('key', 'value');"));
4657 EXPECT_EQ(1, EvalJs(ChildFrameAt(shell(), 0), "localStorage.length"));
4658
4659 // Set up the IPC injection and crash the renderer process so that it's used.
4660 // Without crashing the renderer, the default IPC will be used.
4661 RenderProcessHostImpl::SetDomStorageBinderForTesting(
4662 base::BindRepeating(&CreateTestDomStorageBackendToSaveFirstFrame));
4663 RenderProcessHost* renderer_process_iframe =
4664 ChildFrameAt(shell(), 0)->GetProcess();
4665 RenderProcessHostWatcher crash_observer_iframe(
4666 renderer_process_iframe,
4667 RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
4668 renderer_process_iframe->Shutdown(0);
4669 crash_observer_iframe.Wait();
4670 RenderProcessHost* renderer_process_root =
Dave Tapuska327c06c92022-06-13 20:31:514671 web_contents()->GetPrimaryMainFrame()->GetProcess();
Ari Chivukulaccb16aeb2021-10-01 01:47:124672 RenderProcessHostWatcher crash_observer_root(
4673 renderer_process_root, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
4674 renderer_process_root->Shutdown(0);
4675 crash_observer_root.Wait();
4676
4677 // Re-do tests now that injection is in place
4678 EXPECT_TRUE(NavigateToURL(shell(), isolated_url));
Dave Tapuska327c06c92022-06-13 20:31:514679 EXPECT_EQ(
4680 1, EvalJs(web_contents()->GetPrimaryMainFrame(), "localStorage.length"));
Ari Chivukula15249fc2021-10-01 22:02:524681 content::RenderProcessHostBadIpcMessageWaiter kill_waiter(
4682 ChildFrameAt(shell(), 0)->GetProcess());
Avi Drissman5d5d48d62022-01-07 20:23:584683 std::ignore = ExecJs(ChildFrameAt(shell(), 0), "localStorage.length");
Ari Chivukula15249fc2021-10-01 22:02:524684 EXPECT_EQ(bad_message::RPH_MOJO_PROCESS_ERROR, kill_waiter.Wait());
4685 // The subframe has crashed, but the main frame should still be alive and
4686 // working.
Dave Tapuska327c06c92022-06-13 20:31:514687 EXPECT_EQ(
4688 1, EvalJs(web_contents()->GetPrimaryMainFrame(), "localStorage.length"));
Ari Chivukulaccb16aeb2021-10-01 01:47:124689}
4690
Nasko Oskov59562ccf2017-08-25 03:40:004691// Verify that an isolated renderer process cannot read localStorage of an
4692// origin outside of its isolated site.
Lukasz Anforowiczc62eede2018-12-28 21:52:014693IN_PROC_BROWSER_TEST_F(
4694 IsolatedOriginTest,
4695 LocalStorageOriginEnforcement_IsolatedAccessingNonIsolated) {
Ari Chivukulab54d9042021-09-21 00:27:144696 auto mismatched_storage_key =
4697 blink::StorageKey::CreateFromStringForTesting("http://abc.foo.com");
4698 EXPECT_FALSE(IsIsolatedOrigin(mismatched_storage_key.origin()));
Ari Chivukulaccb16aeb2021-10-01 01:47:124699 RenderProcessHostImpl::SetDomStorageBinderForTesting(
4700 base::BindRepeating(&CreateTestDomStorageBackendToInjectValues,
Arthur Sonzognic686e8f2024-01-11 08:36:374701 mismatched_storage_key, std::nullopt));
Nasko Oskov59562ccf2017-08-25 03:40:004702
4703 GURL isolated_url(
4704 embedded_test_server()->GetURL("isolated.foo.com", "/title1.html"));
Alex Moshchuk8e5c1952019-01-15 03:39:504705 EXPECT_TRUE(IsIsolatedOrigin(url::Origin::Create(isolated_url)));
Lukasz Anforowicz38003582019-09-24 19:08:054706
Lukasz Anforowiczc62eede2018-12-28 21:52:014707 EXPECT_TRUE(NavigateToURL(shell(), isolated_url));
4708
Lukasz Anforowicz1034c0882020-03-21 01:24:534709 content::RenderProcessHostBadIpcMessageWaiter kill_waiter(
Dave Tapuska327c06c92022-06-13 20:31:514710 shell()->web_contents()->GetPrimaryMainFrame()->GetProcess());
Avi Drissman5d5d48d62022-01-07 20:23:584711 // Use std::ignore here, since on Android the renderer process is
Chris Fredricksond3bb2682023-05-10 03:32:264712 // terminated, but ExecJs still returns true. It properly returns
Lukasz Anforowiczc62eede2018-12-28 21:52:014713 // false on all other platforms.
Dave Tapuska327c06c92022-06-13 20:31:514714 std::ignore = ExecJs(shell()->web_contents()->GetPrimaryMainFrame(),
4715 "localStorage.length;");
Lukasz Anforowiczc62eede2018-12-28 21:52:014716 EXPECT_EQ(bad_message::RPH_MOJO_PROCESS_ERROR, kill_waiter.Wait());
4717}
4718
Xiaohan Wang1ecfd002022-01-19 22:33:104719#if BUILDFLAG(IS_ANDROID)
Lukasz Anforowicz38003582019-09-24 19:08:054720#define MAYBE_LocalStorageOriginEnforcement_NonIsolatedAccessingIsolated \
4721 LocalStorageOriginEnforcement_NonIsolatedAccessingIsolated
4722#else
4723// TODO(lukasza): https://crbug.com/566091: Once remote NTP is capable of
4724// embedding OOPIFs, start enforcing citadel-style checks on desktop
4725// platforms.
4726#define MAYBE_LocalStorageOriginEnforcement_NonIsolatedAccessingIsolated \
4727 DISABLED_LocalStorageOriginEnforcement_NonIsolatedAccessingIsolated
4728#endif
4729// Verify that a non-isolated renderer process cannot read localStorage of an
4730// isolated origin.
4731//
4732// TODO(alexmos, lukasza): https://crbug.com/764958: Replicate this test for
4733// the IO-thread case.
4734IN_PROC_BROWSER_TEST_F(
4735 IsolatedOriginTest,
4736 MAYBE_LocalStorageOriginEnforcement_NonIsolatedAccessingIsolated) {
Ari Chivukulab54d9042021-09-21 00:27:144737 auto isolated_storage_key =
4738 blink::StorageKey::CreateFromStringForTesting("http://isolated.foo.com");
4739 EXPECT_TRUE(IsIsolatedOrigin(isolated_storage_key.origin()));
Lukasz Anforowicz38003582019-09-24 19:08:054740
4741 GURL nonisolated_url(
4742 embedded_test_server()->GetURL("non-isolated.com", "/title1.html"));
4743 EXPECT_FALSE(IsIsolatedOrigin(url::Origin::Create(nonisolated_url)));
4744
Ken Rockot8b8424552020-02-20 06:12:414745 RenderProcessHostImpl::SetDomStorageBinderForTesting(
Ari Chivukulaccb16aeb2021-10-01 01:47:124746 base::BindRepeating(&CreateTestDomStorageBackendToInjectValues,
Arthur Sonzognic686e8f2024-01-11 08:36:374747 isolated_storage_key, std::nullopt));
Lukasz Anforowicz38003582019-09-24 19:08:054748 EXPECT_TRUE(NavigateToURL(shell(), nonisolated_url));
4749
Lukasz Anforowicz1034c0882020-03-21 01:24:534750 content::RenderProcessHostBadIpcMessageWaiter kill_waiter(
Dave Tapuska327c06c92022-06-13 20:31:514751 shell()->web_contents()->GetPrimaryMainFrame()->GetProcess());
Avi Drissman5d5d48d62022-01-07 20:23:584752 // Use std::ignore here, since on Android the renderer process is
Chris Fredricksond3bb2682023-05-10 03:32:264753 // terminated, but ExecJs still returns true. It properly returns
Lukasz Anforowicz38003582019-09-24 19:08:054754 // false on all other platforms.
Dave Tapuska327c06c92022-06-13 20:31:514755 std::ignore = ExecJs(shell()->web_contents()->GetPrimaryMainFrame(),
4756 "localStorage.length;");
Lukasz Anforowicz38003582019-09-24 19:08:054757 EXPECT_EQ(bad_message::RPH_MOJO_PROCESS_ERROR, kill_waiter.Wait());
4758}
4759
Lukasz Anforowiczc62eede2018-12-28 21:52:014760// Verify that an IPC request for reading localStorage of an *opaque* origin
4761// will be rejected.
4762IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,
4763 LocalStorageOriginEnforcement_OpaqueOrigin) {
Lukasz Anforowicz7da8b2fd2020-01-24 22:35:014764 url::Origin precursor_origin =
4765 url::Origin::Create(GURL("https://non-isolated.com"));
Ari Chivukulac81e13e2023-02-15 20:44:574766 const blink::StorageKey opaque_storage_key =
4767 blink::StorageKey::CreateFirstParty(
4768 precursor_origin.DeriveNewOpaqueOrigin());
Ken Rockot8b8424552020-02-20 06:12:414769 RenderProcessHostImpl::SetDomStorageBinderForTesting(
Ari Chivukulaccb16aeb2021-10-01 01:47:124770 base::BindRepeating(&CreateTestDomStorageBackendToInjectValues,
Arthur Sonzognic686e8f2024-01-11 08:36:374771 opaque_storage_key, std::nullopt));
Lukasz Anforowiczc62eede2018-12-28 21:52:014772
4773 GURL isolated_url(
4774 embedded_test_server()->GetURL("isolated.foo.com", "/title1.html"));
Alex Moshchuk8e5c1952019-01-15 03:39:504775 EXPECT_TRUE(IsIsolatedOrigin(url::Origin::Create(isolated_url)));
Nasko Oskov59562ccf2017-08-25 03:40:004776 EXPECT_TRUE(NavigateToURL(shell(), isolated_url));
4777
Lukasz Anforowicz1034c0882020-03-21 01:24:534778 content::RenderProcessHostBadIpcMessageWaiter kill_waiter(
Dave Tapuska327c06c92022-06-13 20:31:514779 shell()->web_contents()->GetPrimaryMainFrame()->GetProcess());
Avi Drissman5d5d48d62022-01-07 20:23:584780 // Use std::ignore here, since on Android the renderer process is
Chris Fredricksond3bb2682023-05-10 03:32:264781 // terminated, but ExecJs still returns true. It properly returns
Nasko Oskov59562ccf2017-08-25 03:40:004782 // false on all other platforms.
Dave Tapuska327c06c92022-06-13 20:31:514783 std::ignore = ExecJs(shell()->web_contents()->GetPrimaryMainFrame(),
4784 "localStorage.length;");
Lukasz Anforowicz6f746282018-01-04 23:24:514785 EXPECT_EQ(bad_message::RPH_MOJO_PROCESS_ERROR, kill_waiter.Wait());
Nasko Oskov59562ccf2017-08-25 03:40:004786}
4787
Alex Moshchuk99b795422019-03-07 00:27:324788class IsolatedOriginFieldTrialTest : public IsolatedOriginTestBase {
Lukasz Anforowicz0672f8a2017-11-30 01:07:064789 public:
4790 IsolatedOriginFieldTrialTest() {
4791 scoped_feature_list_.InitAndEnableFeatureWithParameters(
4792 features::kIsolateOrigins,
4793 {{features::kIsolateOriginsFieldTrialParamName,
4794 "https://field.trial.com/,https://bar.com/"}});
4795 }
Sharon Yang034fbb722021-06-23 17:21:324796 ~IsolatedOriginFieldTrialTest() override = default;
4797
4798 IsolatedOriginFieldTrialTest(const IsolatedOriginFieldTrialTest&) = delete;
4799 IsolatedOriginFieldTrialTest& operator=(const IsolatedOriginFieldTrialTest&) =
4800 delete;
Lukasz Anforowicz0672f8a2017-11-30 01:07:064801
4802 private:
4803 base::test::ScopedFeatureList scoped_feature_list_;
Lukasz Anforowicz0672f8a2017-11-30 01:07:064804};
4805
4806IN_PROC_BROWSER_TEST_F(IsolatedOriginFieldTrialTest, Test) {
Lukasz Anforowicz120cd4dc2018-08-14 21:57:334807 bool expected_to_isolate = !base::CommandLine::ForCurrentProcess()->HasSwitch(
Lukasz Anforowicz738a88d2018-11-05 19:19:344808 switches::kDisableSiteIsolation);
Lukasz Anforowicz120cd4dc2018-08-14 21:57:334809
Alex Moshchuk8e5c1952019-01-15 03:39:504810 EXPECT_EQ(expected_to_isolate,
4811 IsIsolatedOrigin(GURL("https://field.trial.com/")));
4812 EXPECT_EQ(expected_to_isolate, IsIsolatedOrigin(GURL("https://bar.com/")));
Lukasz Anforowicz0672f8a2017-11-30 01:07:064813}
4814
Alex Moshchukf886ee2a2018-10-15 20:11:544815class IsolatedOriginCommandLineAndFieldTrialTest
4816 : public IsolatedOriginFieldTrialTest {
4817 public:
4818 IsolatedOriginCommandLineAndFieldTrialTest() = default;
Sharon Yang034fbb722021-06-23 17:21:324819 IsolatedOriginCommandLineAndFieldTrialTest(
4820 const IsolatedOriginCommandLineAndFieldTrialTest&) = delete;
4821 IsolatedOriginCommandLineAndFieldTrialTest& operator=(
4822 const IsolatedOriginCommandLineAndFieldTrialTest&) = delete;
Alex Moshchukf886ee2a2018-10-15 20:11:544823
4824 void SetUpCommandLine(base::CommandLine* command_line) override {
Will Harris2a89f9b52022-01-29 01:09:574825 IsolatedOriginFieldTrialTest::SetUpCommandLine(command_line);
Alex Moshchukf886ee2a2018-10-15 20:11:544826 command_line->AppendSwitchASCII(
4827 switches::kIsolateOrigins,
4828 "https://cmd.line.com/,https://cmdline.com/");
4829 }
Alex Moshchukf886ee2a2018-10-15 20:11:544830};
4831
4832// Verify that the lists of isolated origins specified via --isolate-origins
4833// and via field trials are merged. See https://crbug.com/894535.
4834IN_PROC_BROWSER_TEST_F(IsolatedOriginCommandLineAndFieldTrialTest, Test) {
Alex Moshchukf886ee2a2018-10-15 20:11:544835 // --isolate-origins should take effect regardless of the
Lukasz Anforowicz738a88d2018-11-05 19:19:344836 // kDisableSiteIsolation opt-out flag.
Alex Moshchuk8e5c1952019-01-15 03:39:504837 EXPECT_TRUE(IsIsolatedOrigin(GURL("https://cmd.line.com/")));
4838 EXPECT_TRUE(IsIsolatedOrigin(GURL("https://cmdline.com/")));
Alex Moshchukf886ee2a2018-10-15 20:11:544839
4840 // Field trial origins should also take effect, but only if the opt-out flag
4841 // is not present.
4842 bool expected_to_isolate = !base::CommandLine::ForCurrentProcess()->HasSwitch(
Lukasz Anforowicz738a88d2018-11-05 19:19:344843 switches::kDisableSiteIsolation);
Alex Moshchuk8e5c1952019-01-15 03:39:504844 EXPECT_EQ(expected_to_isolate,
4845 IsIsolatedOrigin(GURL("https://field.trial.com/")));
4846 EXPECT_EQ(expected_to_isolate, IsIsolatedOrigin(GURL("https://bar.com/")));
Alex Moshchukf886ee2a2018-10-15 20:11:544847}
4848
Aaron Colwellddeccbdb2019-03-08 01:11:034849// This is a regression test for https://crbug.com/793350 - the long list of
Lukasz Anforowicz770c0be2018-01-10 15:24:484850// origins to isolate used to be unnecessarily propagated to the renderer
4851// process, trigerring a crash due to exceeding kZygoteMaxMessageLength.
Alex Moshchuk99b795422019-03-07 00:27:324852class IsolatedOriginLongListTest : public IsolatedOriginTestBase {
Lukasz Anforowicz770c0be2018-01-10 15:24:484853 public:
Sharon Yang034fbb722021-06-23 17:21:324854 IsolatedOriginLongListTest() = default;
4855 ~IsolatedOriginLongListTest() override = default;
Lukasz Anforowicz770c0be2018-01-10 15:24:484856
4857 void SetUpCommandLine(base::CommandLine* command_line) override {
4858 ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
4859
4860 std::ostringstream origin_list;
4861 origin_list
4862 << embedded_test_server()->GetURL("isolated.foo.com", "/").spec();
4863 for (int i = 0; i < 1000; i++) {
4864 std::ostringstream hostname;
4865 hostname << "foo" << i << ".com";
4866
4867 origin_list << ","
4868 << embedded_test_server()->GetURL(hostname.str(), "/").spec();
4869 }
4870 command_line->AppendSwitchASCII(switches::kIsolateOrigins,
4871 origin_list.str());
4872 }
4873
4874 void SetUpOnMainThread() override {
4875 host_resolver()->AddRule("*", "127.0.0.1");
4876 embedded_test_server()->StartAcceptingConnections();
4877 }
4878};
4879
4880IN_PROC_BROWSER_TEST_F(IsolatedOriginLongListTest, Test) {
4881 GURL test_url(embedded_test_server()->GetURL(
4882 "bar1.com",
4883 "/cross_site_iframe_factory.html?"
4884 "bar1.com(isolated.foo.com,foo999.com,bar2.com)"));
4885 EXPECT_TRUE(NavigateToURL(shell(), test_url));
4886
Dave Tapuska4eea4112021-09-16 15:49:214887 EXPECT_EQ(4u, CollectAllRenderFrameHosts(shell()->web_contents()).size());
Dave Tapuska327c06c92022-06-13 20:31:514888 RenderFrameHost* main_frame = shell()->web_contents()->GetPrimaryMainFrame();
Dave Tapuska4eea4112021-09-16 15:49:214889 RenderFrameHost* subframe1 = ChildFrameAt(main_frame, 0);
4890 RenderFrameHost* subframe2 = ChildFrameAt(main_frame, 1);
4891 RenderFrameHost* subframe3 = ChildFrameAt(main_frame, 2);
Lukasz Anforowicz770c0be2018-01-10 15:24:484892 EXPECT_EQ("bar1.com", main_frame->GetLastCommittedOrigin().GetURL().host());
4893 EXPECT_EQ("isolated.foo.com",
4894 subframe1->GetLastCommittedOrigin().GetURL().host());
4895 EXPECT_EQ("foo999.com", subframe2->GetLastCommittedOrigin().GetURL().host());
4896 EXPECT_EQ("bar2.com", subframe3->GetLastCommittedOrigin().GetURL().host());
4897
4898 // bar1.com and bar2.com are not on the list of origins to isolate - they
4899 // should stay in the same process, unless --site-per-process has also been
4900 // specified.
4901 if (!AreAllSitesIsolatedForTesting()) {
Emily Andrewsd15fd762024-12-10 20:41:544902 EXPECT_EQ(main_frame->GetProcess()->GetDeprecatedID(),
4903 subframe3->GetProcess()->GetDeprecatedID());
Aaron Colwell5fb878042020-12-17 19:48:444904 if (AreStrictSiteInstancesEnabled()) {
4905 EXPECT_NE(main_frame->GetSiteInstance(), subframe3->GetSiteInstance());
4906 } else {
4907 EXPECT_EQ(main_frame->GetSiteInstance(), subframe3->GetSiteInstance());
4908 }
Lukasz Anforowicz770c0be2018-01-10 15:24:484909 }
4910
4911 // isolated.foo.com and foo999.com are on the list of origins to isolate -
4912 // they should be isolated from everything else.
Emily Andrewsd15fd762024-12-10 20:41:544913 EXPECT_NE(main_frame->GetProcess()->GetDeprecatedID(),
4914 subframe1->GetProcess()->GetDeprecatedID());
Lukasz Anforowicz770c0be2018-01-10 15:24:484915 EXPECT_NE(main_frame->GetSiteInstance(), subframe1->GetSiteInstance());
Emily Andrewsd15fd762024-12-10 20:41:544916 EXPECT_NE(main_frame->GetProcess()->GetDeprecatedID(),
4917 subframe2->GetProcess()->GetDeprecatedID());
Lukasz Anforowicz770c0be2018-01-10 15:24:484918 EXPECT_NE(main_frame->GetSiteInstance(), subframe2->GetSiteInstance());
Emily Andrewsd15fd762024-12-10 20:41:544919 EXPECT_NE(subframe1->GetProcess()->GetDeprecatedID(),
4920 subframe2->GetProcess()->GetDeprecatedID());
Lukasz Anforowicz770c0be2018-01-10 15:24:484921 EXPECT_NE(subframe1->GetSiteInstance(), subframe2->GetSiteInstance());
4922}
4923
Nasko Oskovd83b5712018-05-04 04:50:574924// Check that navigating a subframe to an isolated origin error page puts the
Nasko Oskov0401d812021-02-05 22:20:084925// subframe into an OOPIF and its own SiteInstance. Also check that the error
4926// page in a subframe ends up in the correct SiteInstance.
Nasko Oskovd83b5712018-05-04 04:50:574927IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, SubframeErrorPages) {
4928 GURL top_url(
4929 embedded_test_server()->GetURL("/frame_tree/page_with_two_frames.html"));
4930 GURL isolated_url(
4931 embedded_test_server()->GetURL("isolated.foo.com", "/close-socket"));
4932 GURL regular_url(embedded_test_server()->GetURL("a.com", "/close-socket"));
4933
4934 EXPECT_TRUE(NavigateToURL(shell(), top_url));
Carlos Caballero15caeeb2021-10-27 09:57:554935 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Nasko Oskovd83b5712018-05-04 04:50:574936 EXPECT_EQ(2u, root->child_count());
4937
4938 FrameTreeNode* child1 = root->child_at(0);
4939 FrameTreeNode* child2 = root->child_at(1);
4940
4941 {
4942 TestFrameNavigationObserver observer(child1);
4943 NavigationHandleObserver handle_observer(web_contents(), isolated_url);
Avi Drissmanc91bd8e2021-04-19 23:58:444944 EXPECT_TRUE(
4945 ExecJs(child1, "location.href = '" + isolated_url.spec() + "';"));
Nasko Oskovd83b5712018-05-04 04:50:574946 observer.Wait();
4947 EXPECT_EQ(child1->current_url(), isolated_url);
4948 EXPECT_TRUE(handle_observer.is_error());
4949
4950 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
4951 child1->current_frame_host()->GetSiteInstance());
Nasko Oskov0401d812021-02-05 22:20:084952 if (!SiteIsolationPolicy::IsErrorPageIsolationEnabled(
4953 /*in_main_frame=*/false)) {
4954 EXPECT_EQ(GURL("http://isolated.foo.com/"),
4955 child1->current_frame_host()->GetSiteInstance()->GetSiteURL());
4956 } else {
4957 EXPECT_TRUE(child1->current_frame_host()
4958 ->GetSiteInstance()
4959 ->GetSiteInfo()
4960 .is_error_page());
4961 }
Nasko Oskovd83b5712018-05-04 04:50:574962 }
4963
4964 {
4965 TestFrameNavigationObserver observer(child2);
4966 NavigationHandleObserver handle_observer(web_contents(), regular_url);
4967 EXPECT_TRUE(
Avi Drissmanc91bd8e2021-04-19 23:58:444968 ExecJs(child2, "location.href = '" + regular_url.spec() + "';"));
Nasko Oskovd83b5712018-05-04 04:50:574969 observer.Wait();
4970 EXPECT_EQ(child2->current_url(), regular_url);
4971 EXPECT_TRUE(handle_observer.is_error());
Aaron Colwell5fb878042020-12-17 19:48:444972 if (AreStrictSiteInstancesEnabled()) {
Nasko Oskovd83b5712018-05-04 04:50:574973 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
4974 child2->current_frame_host()->GetSiteInstance());
Nasko Oskov0401d812021-02-05 22:20:084975 if (!SiteIsolationPolicy::IsErrorPageIsolationEnabled(
4976 /*in_main_frame=*/false)) {
4977 EXPECT_EQ(
Aaron Colwell91e32b12021-02-17 01:40:564978 SiteInfo::CreateForTesting(
4979 IsolationContext(web_contents()->GetBrowserContext()),
4980 regular_url),
4981 child2->current_frame_host()->GetSiteInstance()->GetSiteInfo());
Nasko Oskov0401d812021-02-05 22:20:084982 }
Nasko Oskovd83b5712018-05-04 04:50:574983 } else {
4984 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
4985 child2->current_frame_host()->GetSiteInstance());
4986 }
Nasko Oskov0401d812021-02-05 22:20:084987 EXPECT_EQ(SiteIsolationPolicy::IsErrorPageIsolationEnabled(
4988 /*in_main_frame=*/false),
4989 child2->current_frame_host()
4990 ->GetSiteInstance()
4991 ->GetSiteInfo()
4992 .is_error_page());
Nasko Oskovd83b5712018-05-04 04:50:574993 }
4994}
4995
Aaron Colwell7292aec2019-07-25 17:23:434996namespace {
Sharon Yang1a7a63702025-05-23 01:14:554997bool HasDefaultSiteInstanceOrGroup(RenderFrameHost* rfh) {
4998 SiteInstanceImpl* site_instance =
4999 static_cast<SiteInstanceImpl*>(rfh->GetSiteInstance());
5000 if (ShouldUseDefaultSiteInstanceGroup()) {
5001 return site_instance->group() ==
5002 site_instance->DefaultSiteInstanceGroupForBrowsingInstance();
5003 } else {
5004 return site_instance->IsDefaultSiteInstance();
5005 }
Aaron Colwell7292aec2019-07-25 17:23:435006}
5007} // namespace
5008
5009// Verify process assignment behavior for the case where a site that does not
5010// require isolation embeds a frame that does require isolation, which in turn
5011// embeds another site that does not require isolation.
5012// A (Does not require isolation)
5013// +-> B (requires isolation)
5014// +-> C (different site from A that does not require isolation.)
5015// +-> A (same site as top-level which also does not require isolation.)
5016IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, AIsolatedCA) {
Fergal Daly7feb2de2022-06-29 01:31:185017 GURL main_url(
5018 embedded_test_server()->GetURL("www.foo.com",
5019 "/cross_site_iframe_factory.html?www.foo."
5020 "com(isolated.foo.com(c(www.foo.com)))"));
Aaron Colwell7292aec2019-07-25 17:23:435021 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Carlos Caballero15caeeb2021-10-27 09:57:555022 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Aaron Colwell7292aec2019-07-25 17:23:435023 RenderFrameHost* a = root->current_frame_host();
5024 RenderFrameHost* b = root->child_at(0)->current_frame_host();
5025 RenderFrameHost* c = root->child_at(0)->child_at(0)->current_frame_host();
5026 RenderFrameHost* d =
5027 root->child_at(0)->child_at(0)->child_at(0)->current_frame_host();
5028
5029 // Sanity check that the test works with the right frame tree.
5030 EXPECT_FALSE(IsIsolatedOrigin(a->GetLastCommittedOrigin()));
5031 EXPECT_TRUE(IsIsolatedOrigin(b->GetLastCommittedOrigin()));
5032 EXPECT_FALSE(IsIsolatedOrigin(c->GetLastCommittedOrigin()));
5033 EXPECT_FALSE(IsIsolatedOrigin(d->GetLastCommittedOrigin()));
5034 EXPECT_EQ("www.foo.com", a->GetLastCommittedURL().host());
5035 EXPECT_EQ("isolated.foo.com", b->GetLastCommittedURL().host());
5036 EXPECT_EQ("c.com", c->GetLastCommittedURL().host());
5037 EXPECT_EQ("www.foo.com", d->GetLastCommittedURL().host());
5038
5039 // Verify that the isolated site is indeed isolated.
Emily Andrewsd15fd762024-12-10 20:41:545040 EXPECT_NE(b->GetProcess()->GetDeprecatedID(),
5041 a->GetProcess()->GetDeprecatedID());
5042 EXPECT_NE(b->GetProcess()->GetDeprecatedID(),
5043 c->GetProcess()->GetDeprecatedID());
5044 EXPECT_NE(b->GetProcess()->GetDeprecatedID(),
5045 d->GetProcess()->GetDeprecatedID());
Aaron Colwell7292aec2019-07-25 17:23:435046
5047 // Verify that same-origin a and d frames share a process. This is
5048 // necessary for correctness - otherwise a and d wouldn't be able to
5049 // synchronously script each other.
Emily Andrewsd15fd762024-12-10 20:41:545050 EXPECT_EQ(a->GetProcess()->GetDeprecatedID(),
5051 d->GetProcess()->GetDeprecatedID());
Aaron Colwell7292aec2019-07-25 17:23:435052
5053 // Verify that same-origin a and d frames can script each other.
Avi Drissmanc91bd8e2021-04-19 23:58:445054 EXPECT_TRUE(ExecJs(a, "window.name = 'a';"));
5055 EXPECT_TRUE(ExecJs(d, R"(
Aaron Colwell7292aec2019-07-25 17:23:435056 a = window.open('', 'a');
5057 a.cross_frame_property_test = 'hello from d'; )"));
5058 EXPECT_EQ("hello from d",
5059 EvalJs(a, "window.cross_frame_property_test").ExtractString());
5060
5061 // The test assertions below are not strictly necessary - they just document
5062 // the current behavior. In particular, consolidating www.foo.com and c.com
5063 // sites into the same process is not necessary for correctness.
5064 if (AreAllSitesIsolatedForTesting()) {
5065 // All sites are isolated so we expect foo.com, isolated.foo.com and c.com
5066 // to all be in their own processes.
Emily Andrewsd15fd762024-12-10 20:41:545067 EXPECT_NE(a->GetProcess()->GetDeprecatedID(),
5068 b->GetProcess()->GetDeprecatedID());
5069 EXPECT_NE(a->GetProcess()->GetDeprecatedID(),
5070 c->GetProcess()->GetDeprecatedID());
5071 EXPECT_NE(b->GetProcess()->GetDeprecatedID(),
5072 c->GetProcess()->GetDeprecatedID());
Aaron Colwell7292aec2019-07-25 17:23:435073
5074 EXPECT_NE(a->GetSiteInstance(), b->GetSiteInstance());
5075 EXPECT_NE(a->GetSiteInstance(), c->GetSiteInstance());
5076 EXPECT_EQ(a->GetSiteInstance(), d->GetSiteInstance());
5077 EXPECT_NE(b->GetSiteInstance(), c->GetSiteInstance());
5078
Sharon Yang1a7a63702025-05-23 01:14:555079 EXPECT_FALSE(HasDefaultSiteInstanceOrGroup(a));
5080 EXPECT_FALSE(HasDefaultSiteInstanceOrGroup(b));
5081 EXPECT_FALSE(HasDefaultSiteInstanceOrGroup(c));
Sharon Yang02279222025-01-15 19:09:195082 } else {
Aaron Colwell7292aec2019-07-25 17:23:435083 // All sites that are not isolated should be in the same default
5084 // SiteInstance process.
Emily Andrewsd15fd762024-12-10 20:41:545085 EXPECT_NE(a->GetProcess()->GetDeprecatedID(),
5086 b->GetProcess()->GetDeprecatedID());
5087 EXPECT_EQ(a->GetProcess()->GetDeprecatedID(),
5088 c->GetProcess()->GetDeprecatedID());
Aaron Colwell7292aec2019-07-25 17:23:435089
5090 EXPECT_NE(a->GetSiteInstance(), b->GetSiteInstance());
Sharon Yang1a7a63702025-05-23 01:14:555091 if (ShouldUseDefaultSiteInstanceGroup()) {
5092 EXPECT_NE(a->GetSiteInstance(), c->GetSiteInstance());
5093 EXPECT_EQ(a->GetSiteInstance()->GetSiteInstanceGroupId(),
5094 c->GetSiteInstance()->GetSiteInstanceGroupId());
5095 } else {
5096 EXPECT_EQ(a->GetSiteInstance(), c->GetSiteInstance());
5097 }
Aaron Colwell7292aec2019-07-25 17:23:435098 EXPECT_EQ(a->GetSiteInstance(), d->GetSiteInstance());
5099 EXPECT_NE(b->GetSiteInstance(), c->GetSiteInstance());
5100
Sharon Yang1a7a63702025-05-23 01:14:555101 EXPECT_TRUE(HasDefaultSiteInstanceOrGroup(a));
5102 EXPECT_FALSE(HasDefaultSiteInstanceOrGroup(b));
Aaron Colwell7292aec2019-07-25 17:23:435103 }
5104}
5105
Kinuko Yasuda7d925ea22019-08-01 10:08:485106IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, NavigateToBlobURL) {
Marijn Kruisselbrink7a0d5e182018-05-24 22:55:095107 GURL top_url(
5108 embedded_test_server()->GetURL("www.foo.com", "/page_with_iframe.html"));
5109 EXPECT_TRUE(NavigateToURL(shell(), top_url));
5110
5111 GURL isolated_url(embedded_test_server()->GetURL("isolated.foo.com",
5112 "/page_with_iframe.html"));
5113
Carlos Caballero15caeeb2021-10-27 09:57:555114 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Marijn Kruisselbrink7a0d5e182018-05-24 22:55:095115 FrameTreeNode* child = root->child_at(0);
5116
5117 NavigateIframeToURL(web_contents(), "test_iframe", isolated_url);
5118 EXPECT_EQ(child->current_url(), isolated_url);
5119 EXPECT_TRUE(child->current_frame_host()->IsCrossProcessSubframe());
5120
5121 // Now navigate the child frame to a Blob URL.
5122 TestNavigationObserver load_observer(shell()->web_contents());
Dave Tapuska327c06c92022-06-13 20:31:515123 EXPECT_TRUE(ExecJs(shell()->web_contents()->GetPrimaryMainFrame(),
Avi Drissmanc91bd8e2021-04-19 23:58:445124 "const b = new Blob(['foo']);\n"
5125 "const u = URL.createObjectURL(b);\n"
5126 "frames[0].location = u;\n"
5127 "URL.revokeObjectURL(u);"));
Marijn Kruisselbrink7a0d5e182018-05-24 22:55:095128 load_observer.Wait();
5129 EXPECT_TRUE(base::StartsWith(child->current_url().spec(),
5130 "blob:http://www.foo.com",
5131 base::CompareCase::SENSITIVE));
5132 EXPECT_TRUE(load_observer.last_navigation_succeeded());
5133}
5134
Liam Brady38b84562024-03-07 22:11:265135// Test that same-site cross-origin navigations keep user activation even when
5136// origin isolation is enabled.
5137IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, UserActivationSameSite) {
5138 GURL main_url(embedded_test_server()->GetURL(
5139 "a.com", "/cross_site_iframe_factory.html?a(bar)"));
5140 EXPECT_TRUE(NavigateToURL(shell(), main_url));
5141
5142 // It is safe to obtain the root frame tree node here, as it doesn't change.
5143 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
5144 FrameTreeNode* child = root->child_at(0);
5145
5146 // Sanity check that there is no sticky user activation at first.
5147 EXPECT_FALSE(child->current_frame_host()->HasStickyUserActivation());
5148 EXPECT_EQ(false, EvalJs(child->current_frame_host(),
5149 "navigator.userActivation.hasBeenActive",
5150 EXECUTE_SCRIPT_NO_USER_GESTURE));
5151
5152 // Load cross-origin same-site page into iframe and verify there is still no
5153 // sticky user activation.
5154 GURL first_http_url(
5155 embedded_test_server()->GetURL("isolated.bar.com", "/title1.html"));
5156 EXPECT_TRUE(
5157 NavigateToURLFromRendererWithoutUserGesture(child, first_http_url));
5158 EXPECT_FALSE(child->current_frame_host()->HasStickyUserActivation());
5159 EXPECT_EQ(false, EvalJs(child->current_frame_host(),
5160 "navigator.userActivation.hasBeenActive",
5161 EXECUTE_SCRIPT_NO_USER_GESTURE));
5162
5163 // Give the child iframe user activation.
5164 EXPECT_TRUE(ExecJs(child, "// No-op script"));
5165 EXPECT_TRUE(child->current_frame_host()->HasStickyUserActivation());
5166 EXPECT_EQ(true, EvalJs(child->current_frame_host(),
5167 "navigator.userActivation.hasBeenActive",
5168 EXECUTE_SCRIPT_NO_USER_GESTURE));
5169
5170 // Perform another cross-origin same-site navigation in the iframe.
5171 GURL second_http_url(
5172 embedded_test_server()->GetURL("bar.com", "/title1.html"));
5173 EXPECT_TRUE(
5174 NavigateToURLFromRendererWithoutUserGesture(child, second_http_url));
5175
5176 // The cross-origin same-site navigation should keep the sticky user
5177 // activation from the previous page.
5178 EXPECT_TRUE(child->current_frame_host()->HasStickyUserActivation());
5179 EXPECT_EQ(true, EvalJs(child->current_frame_host(),
5180 "navigator.userActivation.hasBeenActive",
5181 EXECUTE_SCRIPT_NO_USER_GESTURE));
5182
5183 // Ensure that top-level navigations can still happen.
5184 EXPECT_TRUE(ExecJs(child->current_frame_host(),
5185 JsReplace("window.open($1, $2)", first_http_url, "_top"),
5186 EXECUTE_SCRIPT_NO_USER_GESTURE));
5187 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
5188 EXPECT_EQ(first_http_url, shell()->web_contents()->GetLastCommittedURL());
5189}
5190
Lukasz Anforowicz738a88d2018-11-05 19:19:345191// Ensure that --disable-site-isolation-trials disables origin isolation.
Nick Carter855bc492018-03-10 00:44:575192class IsolatedOriginTrialOverrideTest : public IsolatedOriginFieldTrialTest {
5193 public:
Sharon Yang034fbb722021-06-23 17:21:325194 IsolatedOriginTrialOverrideTest() = default;
5195 ~IsolatedOriginTrialOverrideTest() override = default;
Nick Carter855bc492018-03-10 00:44:575196
Sharon Yang034fbb722021-06-23 17:21:325197 IsolatedOriginTrialOverrideTest(const IsolatedOriginTrialOverrideTest&) =
5198 delete;
5199 IsolatedOriginTrialOverrideTest& operator=(
5200 const IsolatedOriginTrialOverrideTest&) = delete;
Nick Carter855bc492018-03-10 00:44:575201
5202 void SetUpCommandLine(base::CommandLine* command_line) override {
Will Harris2a89f9b52022-01-29 01:09:575203 IsolatedOriginFieldTrialTest::SetUpCommandLine(command_line);
Lukasz Anforowicz738a88d2018-11-05 19:19:345204 command_line->AppendSwitch(switches::kDisableSiteIsolation);
Nick Carter855bc492018-03-10 00:44:575205 }
Nick Carter855bc492018-03-10 00:44:575206};
5207
5208IN_PROC_BROWSER_TEST_F(IsolatedOriginTrialOverrideTest, Test) {
Jagadesh P1c8699c22023-11-13 14:09:375209 if (AreAllSitesIsolatedForTesting()) {
Nick Carter855bc492018-03-10 00:44:575210 return;
Jagadesh P1c8699c22023-11-13 14:09:375211 }
Alex Moshchuk8e5c1952019-01-15 03:39:505212 EXPECT_FALSE(IsIsolatedOrigin(GURL("https://field.trial.com/")));
5213 EXPECT_FALSE(IsIsolatedOrigin(GURL("https://bar.com/")));
Nick Carter855bc492018-03-10 00:44:575214}
5215
Lukasz Anforowicz87273492018-12-04 22:28:575216// Ensure that --disable-site-isolation-trials and/or
Lukasz Anforowicz569e277a2019-04-26 23:32:195217// --disable-site-isolation-for-policy do not override the flag.
Lukasz Anforowicz87273492018-12-04 22:28:575218class IsolatedOriginPolicyOverrideTest : public IsolatedOriginFieldTrialTest {
5219 public:
Sharon Yang034fbb722021-06-23 17:21:325220 IsolatedOriginPolicyOverrideTest() = default;
5221 ~IsolatedOriginPolicyOverrideTest() override = default;
Lukasz Anforowicz87273492018-12-04 22:28:575222
Sharon Yang034fbb722021-06-23 17:21:325223 IsolatedOriginPolicyOverrideTest(const IsolatedOriginPolicyOverrideTest&) =
5224 delete;
5225 IsolatedOriginPolicyOverrideTest& operator=(
5226 const IsolatedOriginPolicyOverrideTest&) = delete;
Lukasz Anforowicz87273492018-12-04 22:28:575227
5228 void SetUpCommandLine(base::CommandLine* command_line) override {
Will Harris2a89f9b52022-01-29 01:09:575229 IsolatedOriginFieldTrialTest::SetUpCommandLine(command_line);
Lukasz Anforowicz87273492018-12-04 22:28:575230 command_line->AppendSwitch(switches::kDisableSiteIsolation);
Xiaohan Wang1ecfd002022-01-19 22:33:105231#if BUILDFLAG(IS_ANDROID)
Lukasz Anforowicz87273492018-12-04 22:28:575232 command_line->AppendSwitch(switches::kDisableSiteIsolationForPolicy);
Lukasz Anforowicz569e277a2019-04-26 23:32:195233#endif
Lukasz Anforowicz87273492018-12-04 22:28:575234 }
Lukasz Anforowicz87273492018-12-04 22:28:575235};
5236
5237IN_PROC_BROWSER_TEST_F(IsolatedOriginPolicyOverrideTest, Test) {
Jagadesh P1c8699c22023-11-13 14:09:375238 if (AreAllSitesIsolatedForTesting()) {
Lukasz Anforowicz87273492018-12-04 22:28:575239 return;
Jagadesh P1c8699c22023-11-13 14:09:375240 }
Alex Moshchuk8e5c1952019-01-15 03:39:505241 EXPECT_FALSE(IsIsolatedOrigin(GURL("https://field.trial.com/")));
5242 EXPECT_FALSE(IsIsolatedOrigin(GURL("https://bar.com/")));
Lukasz Anforowicz87273492018-12-04 22:28:575243}
5244
5245// Ensure that --disable-site-isolation-trials and/or
Lukasz Anforowicz569e277a2019-04-26 23:32:195246// --disable-site-isolation-for-policy do not override the flag.
Nick Carter855bc492018-03-10 00:44:575247class IsolatedOriginNoFlagOverrideTest : public IsolatedOriginTest {
5248 public:
Sharon Yang034fbb722021-06-23 17:21:325249 IsolatedOriginNoFlagOverrideTest() = default;
5250 ~IsolatedOriginNoFlagOverrideTest() override = default;
Nick Carter855bc492018-03-10 00:44:575251
Sharon Yang034fbb722021-06-23 17:21:325252 IsolatedOriginNoFlagOverrideTest(const IsolatedOriginNoFlagOverrideTest&) =
5253 delete;
5254 IsolatedOriginNoFlagOverrideTest& operator=(
5255 const IsolatedOriginNoFlagOverrideTest&) = delete;
Nick Carter855bc492018-03-10 00:44:575256
5257 void SetUpCommandLine(base::CommandLine* command_line) override {
5258 IsolatedOriginTest::SetUpCommandLine(command_line);
Lukasz Anforowicz738a88d2018-11-05 19:19:345259 command_line->AppendSwitch(switches::kDisableSiteIsolation);
Xiaohan Wang1ecfd002022-01-19 22:33:105260#if BUILDFLAG(IS_ANDROID)
Lukasz Anforowicz87273492018-12-04 22:28:575261 command_line->AppendSwitch(switches::kDisableSiteIsolationForPolicy);
Lukasz Anforowicz569e277a2019-04-26 23:32:195262#endif
Nick Carter855bc492018-03-10 00:44:575263 }
Nick Carter855bc492018-03-10 00:44:575264};
5265
5266IN_PROC_BROWSER_TEST_F(IsolatedOriginNoFlagOverrideTest, Test) {
5267 GURL isolated_url(
5268 embedded_test_server()->GetURL("isolated.foo.com", "/title2.html"));
Alex Moshchuk8e5c1952019-01-15 03:39:505269 EXPECT_TRUE(IsIsolatedOrigin(isolated_url));
5270}
5271
Aaron Colwellddeccbdb2019-03-08 01:11:035272// Verify that main frame's origin isolation still keeps all same-origin frames
5273// in the same process. When allocating processes for a(b(c),d(c)), we should
5274// ensure that "c" frames are in the same process.
5275//
5276// This is a regression test for https://crbug.com/787576.
5277IN_PROC_BROWSER_TEST_F(IsolatedOriginNoFlagOverrideTest,
5278 SameOriginSubframesProcessSharing) {
5279 GURL main_url(embedded_test_server()->GetURL(
Fergal Daly7feb2de2022-06-29 01:31:185280 "isolated.foo.com",
5281 "/cross_site_iframe_factory.html?isolated.foo.com(b(c),d(c))"));
Aaron Colwellddeccbdb2019-03-08 01:11:035282 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Carlos Caballero15caeeb2021-10-27 09:57:555283 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Aaron Colwellddeccbdb2019-03-08 01:11:035284 RenderFrameHost* a = root->current_frame_host();
5285 RenderFrameHost* b = root->child_at(0)->current_frame_host();
5286 RenderFrameHost* c1 = root->child_at(0)->child_at(0)->current_frame_host();
5287 RenderFrameHost* d = root->child_at(1)->current_frame_host();
5288 RenderFrameHost* c2 = root->child_at(1)->child_at(0)->current_frame_host();
5289
5290 // Sanity check that the test works with the right frame tree.
5291 EXPECT_TRUE(IsIsolatedOrigin(a->GetLastCommittedOrigin()));
5292 EXPECT_FALSE(IsIsolatedOrigin(b->GetLastCommittedOrigin()));
5293 EXPECT_FALSE(IsIsolatedOrigin(d->GetLastCommittedOrigin()));
5294 EXPECT_FALSE(IsIsolatedOrigin(c1->GetLastCommittedOrigin()));
5295 EXPECT_FALSE(IsIsolatedOrigin(c2->GetLastCommittedOrigin()));
5296 EXPECT_EQ("b.com", b->GetLastCommittedURL().host());
5297 EXPECT_EQ("d.com", d->GetLastCommittedURL().host());
5298 EXPECT_EQ("c.com", c1->GetLastCommittedURL().host());
5299 EXPECT_EQ("c.com", c2->GetLastCommittedURL().host());
5300
5301 // Verify that the isolated site is indeed isolated.
Emily Andrewsd15fd762024-12-10 20:41:545302 EXPECT_NE(a->GetProcess()->GetDeprecatedID(),
5303 c1->GetProcess()->GetDeprecatedID());
5304 EXPECT_NE(a->GetProcess()->GetDeprecatedID(),
5305 c2->GetProcess()->GetDeprecatedID());
5306 EXPECT_NE(a->GetProcess()->GetDeprecatedID(),
5307 b->GetProcess()->GetDeprecatedID());
5308 EXPECT_NE(a->GetProcess()->GetDeprecatedID(),
5309 d->GetProcess()->GetDeprecatedID());
Aaron Colwellddeccbdb2019-03-08 01:11:035310
5311 // Verify that same-origin c1 and c2 frames share a process. This is
5312 // necessary for correctness - otherwise c1 and c2 wouldn't be able to
5313 // synchronously script each other.
Emily Andrewsd15fd762024-12-10 20:41:545314 EXPECT_EQ(c1->GetProcess()->GetDeprecatedID(),
5315 c2->GetProcess()->GetDeprecatedID());
Aaron Colwellddeccbdb2019-03-08 01:11:035316
5317 // Verify that same-origin c1 and c2 frames can script each other.
Avi Drissmanc91bd8e2021-04-19 23:58:445318 EXPECT_TRUE(ExecJs(c1, "window.name = 'c1';"));
5319 EXPECT_TRUE(ExecJs(c2, R"(
Aaron Colwellddeccbdb2019-03-08 01:11:035320 c1 = window.open('', 'c1');
5321 c1.cross_frame_property_test = 'hello from c2'; )"));
Avi Drissmanc91bd8e2021-04-19 23:58:445322 EXPECT_EQ("hello from c2", EvalJs(c1, "window.cross_frame_property_test;"));
Aaron Colwellddeccbdb2019-03-08 01:11:035323
5324 // The test assertions below are not strictly necessary - they just document
5325 // the current behavior and might be tweaked if needed. In particular,
5326 // consolidating b,c,d sites into the same process is not necessary for
5327 // correctness. Consolidation might be desirable if we want to limit the
5328 // number of renderer processes. OTOH, consolidation might be undesirable
5329 // if we desire smaller renderer processes (even if it means more processes).
5330 if (!AreAllSitesIsolatedForTesting()) {
Emily Andrewsd15fd762024-12-10 20:41:545331 EXPECT_EQ(b->GetProcess()->GetDeprecatedID(),
5332 c1->GetProcess()->GetDeprecatedID());
5333 EXPECT_EQ(b->GetProcess()->GetDeprecatedID(),
5334 c2->GetProcess()->GetDeprecatedID());
5335 EXPECT_EQ(b->GetProcess()->GetDeprecatedID(),
5336 d->GetProcess()->GetDeprecatedID());
Aaron Colwellddeccbdb2019-03-08 01:11:035337 } else {
Emily Andrewsd15fd762024-12-10 20:41:545338 EXPECT_NE(b->GetProcess()->GetDeprecatedID(),
5339 c1->GetProcess()->GetDeprecatedID());
5340 EXPECT_NE(b->GetProcess()->GetDeprecatedID(),
5341 c2->GetProcess()->GetDeprecatedID());
5342 EXPECT_NE(b->GetProcess()->GetDeprecatedID(),
5343 d->GetProcess()->GetDeprecatedID());
5344 EXPECT_EQ(c1->GetProcess()->GetDeprecatedID(),
5345 c2->GetProcess()->GetDeprecatedID());
Aaron Colwellddeccbdb2019-03-08 01:11:035346 }
5347}
5348
Alex Moshchuk8e5c1952019-01-15 03:39:505349// Helper class for testing dynamically-added isolated origins. Tests that use
5350// this run without full --site-per-process, but with two isolated origins that
5351// are configured at startup (isolated.foo.com and isolated.bar.com).
5352class DynamicIsolatedOriginTest : public IsolatedOriginTest {
5353 public:
Lily Chen9f7dac52019-10-01 16:29:095354 DynamicIsolatedOriginTest()
5355 : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
Sharon Yang034fbb722021-06-23 17:21:325356 ~DynamicIsolatedOriginTest() override = default;
5357
5358 DynamicIsolatedOriginTest(const DynamicIsolatedOriginTest&) = delete;
5359 DynamicIsolatedOriginTest& operator=(const DynamicIsolatedOriginTest&) =
5360 delete;
Alex Moshchuk8e5c1952019-01-15 03:39:505361
5362 void SetUpCommandLine(base::CommandLine* command_line) override {
5363 IsolatedOriginTest::SetUpCommandLine(command_line);
5364 command_line->AppendSwitch(switches::kDisableSiteIsolation);
Alex Moshchukbb00c142019-02-06 00:34:595365
5366 if (AreAllSitesIsolatedForTesting()) {
5367 LOG(WARNING) << "This test should be run without strict site isolation. "
5368 << "It does nothing when --site-per-process is specified.";
5369 }
Alex Moshchuk8e5c1952019-01-15 03:39:505370 }
5371
Lily Chen9f7dac52019-10-01 16:29:095372 void SetUpOnMainThread() override {
5373 https_server()->AddDefaultHandlers(GetTestDataFilePath());
5374 ASSERT_TRUE(https_server()->Start());
5375 IsolatedOriginTest::SetUpOnMainThread();
5376 }
5377
5378 // Need an https server because third-party cookies are used, and
5379 // SameSite=None cookies must be Secure.
5380 net::EmbeddedTestServer* https_server() { return &https_server_; }
5381
Alex Moshchuk8e5c1952019-01-15 03:39:505382 private:
Lily Chen9f7dac52019-10-01 16:29:095383 net::EmbeddedTestServer https_server_;
Alex Moshchuk8e5c1952019-01-15 03:39:505384};
5385
5386// Check that dynamically added isolated origins take effect for future
5387// BrowsingInstances only.
5388IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest,
5389 IsolationAppliesToFutureBrowsingInstances) {
Alex Moshchukbb00c142019-02-06 00:34:595390 // This test is designed to run without strict site isolation.
Jagadesh P1c8699c22023-11-13 14:09:375391 if (AreAllSitesIsolatedForTesting()) {
Alex Moshchukbb00c142019-02-06 00:34:595392 return;
Jagadesh P1c8699c22023-11-13 14:09:375393 }
Alex Moshchukbb00c142019-02-06 00:34:595394
Alex Moshchuk8e5c1952019-01-15 03:39:505395 // Start on a non-isolated origin with same-site iframe.
5396 GURL foo_url(
5397 embedded_test_server()->GetURL("foo.com", "/page_with_iframe.html"));
5398 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
5399
Carlos Caballero15caeeb2021-10-27 09:57:555400 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Alex Moshchuk8e5c1952019-01-15 03:39:505401 FrameTreeNode* child = root->child_at(0);
5402
5403 // Navigate iframe cross-site.
5404 GURL bar_url(embedded_test_server()->GetURL("bar.com", "/title1.html"));
5405 NavigateIframeToURL(web_contents(), "test_iframe", bar_url);
5406 EXPECT_EQ(child->current_url(), bar_url);
5407
5408 // The two frames should be in the same process, since neither site is
5409 // isolated so far.
Aaron Colwell5fb878042020-12-17 19:48:445410 if (AreStrictSiteInstancesEnabled()) {
5411 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
5412 child->current_frame_host()->GetSiteInstance());
5413 } else {
Alex Moshchuk8e5c1952019-01-15 03:39:505414 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
5415 child->current_frame_host()->GetSiteInstance());
Alex Moshchuk8e5c1952019-01-15 03:39:505416 }
Aaron Colwell5fb878042020-12-17 19:48:445417 EXPECT_EQ(root->current_frame_host()->GetProcess(),
5418 child->current_frame_host()->GetProcess());
Alex Moshchuk8e5c1952019-01-15 03:39:505419
5420 // Start isolating foo.com.
Nick Carter855bc492018-03-10 00:44:575421 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
Alex Moshchukef8c2562021-03-12 06:37:455422 policy->AddFutureIsolatedOrigins({url::Origin::Create(foo_url)},
5423 IsolatedOriginSource::TEST);
Alex Moshchuk8e5c1952019-01-15 03:39:505424
5425 // The isolation shouldn't take effect in the current frame tree, so that it
5426 // doesn't break same-site scripting. Navigate iframe to a foo.com URL and
5427 // ensure it stays in the same process.
5428 NavigateIframeToURL(web_contents(), "test_iframe", foo_url);
5429 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
5430 child->current_frame_host()->GetSiteInstance());
5431 EXPECT_EQ(root->current_frame_host()->GetProcess(),
5432 child->current_frame_host()->GetProcess());
5433
5434 // Also try a foo(bar(foo)) hierarchy and check that all frames are still in
5435 // the same SiteInstance/process.
5436 GURL bar_with_foo_url(embedded_test_server()->GetURL(
5437 "bar.com", "/cross_site_iframe_factory.html?bar.com(foo.com)"));
5438 NavigateIframeToURL(web_contents(), "test_iframe", bar_with_foo_url);
5439 FrameTreeNode* grandchild = child->child_at(0);
Aaron Colwell5fb878042020-12-17 19:48:445440 if (AreStrictSiteInstancesEnabled()) {
5441 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
5442 child->current_frame_host()->GetSiteInstance());
5443 EXPECT_NE(child->current_frame_host()->GetSiteInstance(),
5444 grandchild->current_frame_host()->GetSiteInstance());
5445 } else {
5446 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
5447 child->current_frame_host()->GetSiteInstance());
5448 EXPECT_EQ(child->current_frame_host()->GetSiteInstance(),
5449 grandchild->current_frame_host()->GetSiteInstance());
5450 }
Alex Moshchuk8e5c1952019-01-15 03:39:505451 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
5452 grandchild->current_frame_host()->GetSiteInstance());
Aaron Colwell5fb878042020-12-17 19:48:445453 EXPECT_EQ(root->current_frame_host()->GetProcess(),
5454 child->current_frame_host()->GetProcess());
5455 EXPECT_EQ(child->current_frame_host()->GetProcess(),
5456 grandchild->current_frame_host()->GetProcess());
Alex Moshchuk8e5c1952019-01-15 03:39:505457
5458 // Create an unrelated window, which will be in a new BrowsingInstance.
5459 // Ensure that foo.com becomes an isolated origin in that window. A
5460 // cross-site bar.com subframe on foo.com should now become an OOPIF.
5461 Shell* second_shell = CreateBrowser();
5462 EXPECT_TRUE(NavigateToURL(second_shell, foo_url));
5463
5464 FrameTreeNode* second_root =
5465 static_cast<WebContentsImpl*>(second_shell->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:555466 ->GetPrimaryFrameTree()
5467 .root();
Alex Moshchuk8e5c1952019-01-15 03:39:505468 FrameTreeNode* second_child = second_root->child_at(0);
5469
5470 NavigateIframeToURL(second_shell->web_contents(), "test_iframe", bar_url);
5471 scoped_refptr<SiteInstance> foo_instance =
5472 second_root->current_frame_host()->GetSiteInstance();
5473 EXPECT_NE(foo_instance,
5474 second_child->current_frame_host()->GetSiteInstance());
5475 EXPECT_NE(second_root->current_frame_host()->GetProcess(),
5476 second_child->current_frame_host()->GetProcess());
5477
5478 // Now try the reverse: ensure that when bar.com embeds foo.com, foo.com
5479 // becomes an OOPIF.
5480 EXPECT_TRUE(NavigateToURL(second_shell, bar_with_foo_url));
5481
5482 // We should've swapped processes in the main frame, since we navigated from
5483 // (isolated) foo.com to (non-isolated) bar.com.
5484 EXPECT_NE(foo_instance, second_root->current_frame_host()->GetSiteInstance());
5485
5486 // Ensure the new foo.com subframe is cross-process.
5487 second_child = second_root->child_at(0);
5488 EXPECT_NE(second_root->current_frame_host()->GetSiteInstance(),
5489 second_child->current_frame_host()->GetSiteInstance());
5490 EXPECT_NE(second_root->current_frame_host()->GetProcess(),
5491 second_child->current_frame_host()->GetProcess());
5492}
5493
5494// Check that dynamically added isolated origins take effect for future
5495// BrowsingInstances only, focusing on various main frame navigations.
5496IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest, MainFrameNavigations) {
Alex Moshchukbb00c142019-02-06 00:34:595497 // This test is designed to run without strict site isolation.
Jagadesh P1c8699c22023-11-13 14:09:375498 if (AreAllSitesIsolatedForTesting()) {
Alex Moshchukbb00c142019-02-06 00:34:595499 return;
Jagadesh P1c8699c22023-11-13 14:09:375500 }
Alex Moshchukbb00c142019-02-06 00:34:595501
Alex Moshchuk8e5c1952019-01-15 03:39:505502 // Create three windows on a non-isolated origin.
5503 GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
5504 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
5505
5506 Shell* shell2 = CreateBrowser();
5507 EXPECT_TRUE(NavigateToURL(shell2, foo_url));
5508
5509 Shell* shell3 = CreateBrowser();
5510 EXPECT_TRUE(NavigateToURL(shell3, foo_url));
5511
Alex Moshchukdc3560cb2019-04-17 22:05:075512 // Create window.open popups in all three windows, which would prevent a
5513 // BrowsingInstance swap on renderer-initiated navigations to newly isolated
5514 // origins in these windows.
5515 OpenPopup(shell(), foo_url, "");
5516 OpenPopup(shell2, GURL(url::kAboutBlankURL), "");
5517 OpenPopup(shell3, embedded_test_server()->GetURL("baz.com", "/title1.html"),
5518 "");
5519
Alex Moshchuk8e5c1952019-01-15 03:39:505520 // Start isolating bar.com.
5521 GURL bar_url(embedded_test_server()->GetURL("bar.com", "/title2.html"));
5522 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
Alex Moshchukef8c2562021-03-12 06:37:455523 policy->AddFutureIsolatedOrigins({url::Origin::Create(bar_url)},
5524 IsolatedOriginSource::TEST);
Alex Moshchuk8e5c1952019-01-15 03:39:505525
5526 // Do a renderer-initiated navigation in each of the existing three windows.
5527 // None of them should swap to a new process, since bar.com shouldn't be
5528 // isolated in those older BrowsingInstances.
Dave Tapuska327c06c92022-06-13 20:31:515529 int old_process_id =
Emily Andrewsd15fd762024-12-10 20:41:545530 web_contents()->GetPrimaryMainFrame()->GetProcess()->GetDeprecatedID();
Alex Moshchuk8e5c1952019-01-15 03:39:505531 EXPECT_TRUE(NavigateToURLFromRenderer(shell(), bar_url));
Emily Andrewsd15fd762024-12-10 20:41:545532 EXPECT_EQ(
5533 old_process_id,
5534 web_contents()->GetPrimaryMainFrame()->GetProcess()->GetDeprecatedID());
Alex Moshchuk8e5c1952019-01-15 03:39:505535
Emily Andrewsd15fd762024-12-10 20:41:545536 old_process_id = shell2->web_contents()
5537 ->GetPrimaryMainFrame()
5538 ->GetProcess()
5539 ->GetDeprecatedID();
Alex Moshchuk8e5c1952019-01-15 03:39:505540 EXPECT_TRUE(NavigateToURLFromRenderer(shell2, bar_url));
Emily Andrewsd15fd762024-12-10 20:41:545541 EXPECT_EQ(old_process_id, shell2->web_contents()
5542 ->GetPrimaryMainFrame()
5543 ->GetProcess()
5544 ->GetDeprecatedID());
Alex Moshchuk8e5c1952019-01-15 03:39:505545
Emily Andrewsd15fd762024-12-10 20:41:545546 old_process_id = shell3->web_contents()
5547 ->GetPrimaryMainFrame()
5548 ->GetProcess()
5549 ->GetDeprecatedID();
Alex Moshchuk8e5c1952019-01-15 03:39:505550 EXPECT_TRUE(NavigateToURLFromRenderer(shell3, bar_url));
Emily Andrewsd15fd762024-12-10 20:41:545551 EXPECT_EQ(old_process_id, shell3->web_contents()
5552 ->GetPrimaryMainFrame()
5553 ->GetProcess()
5554 ->GetDeprecatedID());
Alex Moshchuk8e5c1952019-01-15 03:39:505555
5556 // Now try the same in a new window and BrowsingInstance, and ensure that the
5557 // navigation to bar.com swaps processes in that case.
5558 Shell* shell4 = CreateBrowser();
5559 EXPECT_TRUE(NavigateToURL(shell4, foo_url));
5560
Emily Andrewsd15fd762024-12-10 20:41:545561 old_process_id = shell4->web_contents()
5562 ->GetPrimaryMainFrame()
5563 ->GetProcess()
5564 ->GetDeprecatedID();
Alex Moshchuk8e5c1952019-01-15 03:39:505565 EXPECT_TRUE(NavigateToURLFromRenderer(shell4, bar_url));
Emily Andrewsd15fd762024-12-10 20:41:545566 EXPECT_NE(old_process_id, shell4->web_contents()
5567 ->GetPrimaryMainFrame()
5568 ->GetProcess()
5569 ->GetDeprecatedID());
Alex Moshchuk8e5c1952019-01-15 03:39:505570
5571 // Go back to foo.com in window 1, ensuring this stays in the same process.
5572 {
Dave Tapuska327c06c92022-06-13 20:31:515573 old_process_id =
Emily Andrewsd15fd762024-12-10 20:41:545574 web_contents()->GetPrimaryMainFrame()->GetProcess()->GetDeprecatedID();
Alex Moshchuk8e5c1952019-01-15 03:39:505575 TestNavigationObserver back_observer(web_contents());
5576 web_contents()->GetController().GoBack();
5577 back_observer.Wait();
Emily Andrewsd15fd762024-12-10 20:41:545578 EXPECT_EQ(
5579 old_process_id,
5580 web_contents()->GetPrimaryMainFrame()->GetProcess()->GetDeprecatedID());
Alex Moshchuk8e5c1952019-01-15 03:39:505581 }
5582
5583 // Go back to foo.com in window 4, ensuring this swaps processes.
5584 {
Emily Andrewsd15fd762024-12-10 20:41:545585 old_process_id = shell4->web_contents()
5586 ->GetPrimaryMainFrame()
5587 ->GetProcess()
5588 ->GetDeprecatedID();
Alex Moshchuk8e5c1952019-01-15 03:39:505589 TestNavigationObserver back_observer(shell4->web_contents());
5590 shell4->web_contents()->GetController().GoBack();
5591 back_observer.Wait();
Emily Andrewsd15fd762024-12-10 20:41:545592 EXPECT_NE(old_process_id, shell4->web_contents()
5593 ->GetPrimaryMainFrame()
5594 ->GetProcess()
5595 ->GetDeprecatedID());
Alex Moshchuk8e5c1952019-01-15 03:39:505596 }
5597}
5598
5599// Check that dynamically added isolated origins do not prevent older processes
5600// for the same origin from accessing cookies.
5601IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest, OldProcessCanAccessCookies) {
Alex Moshchukbb00c142019-02-06 00:34:595602 // This test is designed to run without strict site isolation.
Jagadesh P1c8699c22023-11-13 14:09:375603 if (AreAllSitesIsolatedForTesting()) {
Alex Moshchukbb00c142019-02-06 00:34:595604 return;
Jagadesh P1c8699c22023-11-13 14:09:375605 }
Alex Moshchukbb00c142019-02-06 00:34:595606
Alex Moshchuk8e5c1952019-01-15 03:39:505607 GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
5608 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
Carlos Caballero15caeeb2021-10-27 09:57:555609 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Alex Moshchuk8e5c1952019-01-15 03:39:505610
Aaron Colwellab4e5a22020-09-19 01:07:525611 // Since foo.com isn't isolated yet, its process lock should allow any site.
Alex Moshchuk8e5c1952019-01-15 03:39:505612 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
Sharon Yang57481e52021-12-01 00:05:225613 EXPECT_TRUE(root->current_frame_host()
5614 ->GetProcess()
5615 ->GetProcessLock()
5616 .allows_any_site());
Alex Moshchuk8e5c1952019-01-15 03:39:505617
5618 // Start isolating foo.com.
Alex Moshchukef8c2562021-03-12 06:37:455619 policy->AddFutureIsolatedOrigins({url::Origin::Create(foo_url)},
5620 IsolatedOriginSource::TEST);
Alex Moshchuk8e5c1952019-01-15 03:39:505621
5622 // Create an unrelated window, which will be in a new BrowsingInstance.
5623 // foo.com will become an isolated origin in that window.
5624 Shell* second_shell = CreateBrowser();
5625 EXPECT_TRUE(NavigateToURL(second_shell, foo_url));
5626 FrameTreeNode* second_root =
5627 static_cast<WebContentsImpl*>(second_shell->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:555628 ->GetPrimaryFrameTree()
5629 .root();
Alex Moshchuk8e5c1952019-01-15 03:39:505630
5631 // The new window's process should be locked to "foo.com".
5632 int isolated_foo_com_process_id =
Emily Andrewsd15fd762024-12-10 20:41:545633 second_root->current_frame_host()->GetProcess()->GetDeprecatedID();
W. James MacLeane84fa112020-07-14 17:25:545634 EXPECT_EQ(ProcessLockFromUrl("http://foo.com"),
5635 policy->GetProcessLock(isolated_foo_com_process_id));
Alex Moshchuk8e5c1952019-01-15 03:39:505636
5637 // Make sure both old and new foo.com processes can access cookies without
5638 // renderer kills.
Avi Drissmanc91bd8e2021-04-19 23:58:445639 EXPECT_TRUE(ExecJs(root, "document.cookie = 'foo=bar';"));
Alex Moshchuk8e5c1952019-01-15 03:39:505640 EXPECT_EQ("foo=bar", EvalJs(root, "document.cookie"));
Avi Drissmanc91bd8e2021-04-19 23:58:445641 EXPECT_TRUE(ExecJs(second_root, "document.cookie = 'foo=bar';"));
Alex Moshchuk8e5c1952019-01-15 03:39:505642 EXPECT_EQ("foo=bar", EvalJs(second_root, "document.cookie"));
5643
Ari Chivukula5350aad92021-08-10 02:42:245644 // Navigate to sub.foo.com in `second_shell`, staying in same
Alex Moshchuk8e5c1952019-01-15 03:39:505645 // BrowsingInstance. This should stay in the same process.
5646 GURL sub_foo_url(
5647 embedded_test_server()->GetURL("sub.foo.com", "/title1.html"));
5648 EXPECT_TRUE(NavigateToURLInSameBrowsingInstance(second_shell, sub_foo_url));
5649 EXPECT_EQ(isolated_foo_com_process_id,
Emily Andrewsd15fd762024-12-10 20:41:545650 second_root->current_frame_host()->GetProcess()->GetDeprecatedID());
Alex Moshchuk8e5c1952019-01-15 03:39:505651
5652 // Now, start isolating sub.foo.com.
Alex Moshchukef8c2562021-03-12 06:37:455653 policy->AddFutureIsolatedOrigins({url::Origin::Create(sub_foo_url)},
5654 IsolatedOriginSource::TEST);
Alex Moshchuk8e5c1952019-01-15 03:39:505655
5656 // Make sure the process locked to foo.com, which currently has sub.foo.com
5657 // committed in it, can still access sub.foo.com cookies.
Avi Drissmanc91bd8e2021-04-19 23:58:445658 EXPECT_TRUE(ExecJs(second_root, "document.cookie = 'foo=baz';"));
Alex Moshchuk8e5c1952019-01-15 03:39:505659 EXPECT_EQ("foo=baz", EvalJs(second_root, "document.cookie"));
5660
5661 // Now, navigate to sub.foo.com in a new BrowsingInstance. This should go
5662 // into a new process, locked to sub.foo.com.
5663 // TODO(alexmos): navigating to bar.com prior to navigating to sub.foo.com is
5664 // currently needed since we only swap BrowsingInstances on cross-site
5665 // address bar navigations. We should look into swapping BrowsingInstances
5666 // even on same-site browser-initiated navigations, in cases where the sites
5667 // change due to a dynamically isolated origin.
5668 EXPECT_TRUE(NavigateToURL(
5669 second_shell, embedded_test_server()->GetURL("bar.com", "/title2.html")));
5670 EXPECT_TRUE(NavigateToURL(second_shell, sub_foo_url));
5671 EXPECT_NE(isolated_foo_com_process_id,
Emily Andrewsd15fd762024-12-10 20:41:545672 second_root->current_frame_host()->GetProcess()->GetDeprecatedID());
W. James MacLeane84fa112020-07-14 17:25:545673 EXPECT_EQ(ProcessLockFromUrl("http://sub.foo.com"),
Sharon Yang57481e52021-12-01 00:05:225674 second_root->current_frame_host()->GetProcess()->GetProcessLock());
Alex Moshchuk8e5c1952019-01-15 03:39:505675
5676 // Make sure that process can also access sub.foo.com cookies.
Avi Drissmanc91bd8e2021-04-19 23:58:445677 EXPECT_TRUE(ExecJs(second_root, "document.cookie = 'foo=qux';"));
Alex Moshchuk8e5c1952019-01-15 03:39:505678 EXPECT_EQ("foo=qux", EvalJs(second_root, "document.cookie"));
5679}
5680
5681// Verify that when isolating sub.foo.com dynamically, foo.com and sub.foo.com
5682// start to be treated as cross-site for process model decisions.
5683IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest, IsolatedSubdomain) {
Alex Moshchukbb00c142019-02-06 00:34:595684 // This test is designed to run without strict site isolation.
Jagadesh P1c8699c22023-11-13 14:09:375685 if (AreAllSitesIsolatedForTesting()) {
Alex Moshchukbb00c142019-02-06 00:34:595686 return;
Jagadesh P1c8699c22023-11-13 14:09:375687 }
Alex Moshchukbb00c142019-02-06 00:34:595688
Alex Moshchuk8e5c1952019-01-15 03:39:505689 GURL foo_url(
5690 embedded_test_server()->GetURL("foo.com", "/page_with_iframe.html"));
5691 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
5692
5693 // Start isolating sub.foo.com.
5694 GURL sub_foo_url(
5695 embedded_test_server()->GetURL("sub.foo.com", "/title1.html"));
5696 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
Alex Moshchukef8c2562021-03-12 06:37:455697 policy->AddFutureIsolatedOrigins({url::Origin::Create(sub_foo_url)},
5698 IsolatedOriginSource::TEST);
Alex Moshchuk8e5c1952019-01-15 03:39:505699
5700 // Navigate to foo.com and then to sub.foo.com in a new BrowsingInstance.
5701 // foo.com and sub.foo.com should now be considered cross-site for the
5702 // purposes of process assignment, and we should swap processes.
5703 Shell* new_shell = CreateBrowser();
5704 EXPECT_TRUE(NavigateToURL(new_shell, foo_url));
Emily Andrewsd15fd762024-12-10 20:41:545705 int initial_process_id = new_shell->web_contents()
5706 ->GetPrimaryMainFrame()
5707 ->GetProcess()
5708 ->GetDeprecatedID();
Alex Moshchuk8e5c1952019-01-15 03:39:505709 EXPECT_TRUE(NavigateToURLFromRenderer(new_shell, sub_foo_url));
Emily Andrewsd15fd762024-12-10 20:41:545710 EXPECT_NE(initial_process_id, new_shell->web_contents()
5711 ->GetPrimaryMainFrame()
5712 ->GetProcess()
5713 ->GetDeprecatedID());
Alex Moshchuk8e5c1952019-01-15 03:39:505714
5715 // Repeat this, but now navigate a subframe on foo.com to sub.foo.com and
5716 // ensure that it is rendered in an OOPIF.
5717 new_shell = CreateBrowser();
5718 EXPECT_TRUE(NavigateToURL(new_shell, foo_url));
5719 NavigateIframeToURL(new_shell->web_contents(), "test_iframe", sub_foo_url);
5720 FrameTreeNode* root = static_cast<WebContentsImpl*>(new_shell->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:555721 ->GetPrimaryFrameTree()
5722 .root();
Alex Moshchuk8e5c1952019-01-15 03:39:505723 FrameTreeNode* child = root->child_at(0);
5724
5725 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
5726 child->current_frame_host()->GetSiteInstance());
5727 EXPECT_NE(root->current_frame_host()->GetProcess(),
5728 child->current_frame_host()->GetProcess());
5729}
5730
5731// Check that when an isolated origin takes effect in BrowsingInstance 1, a new
5732// BrowsingInstance 2, which reuses an old process from BrowsingInstance 1 for
5733// its main frame, still applies the isolated origin to its subframe. This
5734// demonstrates that isolated origins can't be scoped purely based on process
5735// IDs.
5736IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest,
5737 NewBrowsingInstanceInOldProcess) {
Alex Moshchukbb00c142019-02-06 00:34:595738 // This test is designed to run without strict site isolation.
Jagadesh P1c8699c22023-11-13 14:09:375739 if (AreAllSitesIsolatedForTesting()) {
Alex Moshchuk989aa22c2024-11-06 02:09:075740 GTEST_SKIP();
Jagadesh P1c8699c22023-11-13 14:09:375741 }
Alex Moshchukbb00c142019-02-06 00:34:595742
Alex Moshchuk8e5c1952019-01-15 03:39:505743 // Force process reuse for main frames in new BrowsingInstances.
5744 RenderProcessHost::SetMaxRendererProcessCount(1);
5745
5746 // Start on a non-isolated origin with same-site iframe.
Lily Chen9f7dac52019-10-01 16:29:095747 GURL foo_url(https_server()->GetURL("foo.com", "/page_with_iframe.html"));
Alex Moshchuk8e5c1952019-01-15 03:39:505748 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
5749
Carlos Caballero15caeeb2021-10-27 09:57:555750 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Alex Moshchuk8e5c1952019-01-15 03:39:505751 FrameTreeNode* child = root->child_at(0);
5752
5753 // Navigate iframe cross-site.
Lily Chen9f7dac52019-10-01 16:29:095754 GURL bar_url(https_server()->GetURL("bar.com", "/title1.html"));
Alex Moshchuk8e5c1952019-01-15 03:39:505755 NavigateIframeToURL(web_contents(), "test_iframe", bar_url);
5756 EXPECT_EQ(child->current_url(), bar_url);
5757
5758 // The iframe should not be in an OOPIF yet.
Aaron Colwell5fb878042020-12-17 19:48:445759 if (AreStrictSiteInstancesEnabled()) {
5760 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
5761 child->current_frame_host()->GetSiteInstance());
5762
5763 } else {
5764 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
5765 child->current_frame_host()->GetSiteInstance());
5766 }
Alex Moshchuk8e5c1952019-01-15 03:39:505767 EXPECT_EQ(root->current_frame_host()->GetProcess(),
5768 child->current_frame_host()->GetProcess());
5769
5770 // Start isolating bar.com.
5771 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
Alex Moshchukef8c2562021-03-12 06:37:455772 policy->AddFutureIsolatedOrigins({url::Origin::Create(bar_url)},
5773 IsolatedOriginSource::TEST);
Alex Moshchuk8e5c1952019-01-15 03:39:505774
5775 // Open a new window in a new BrowsingInstance. Navigate to foo.com and
5776 // check that the old foo.com process is reused.
5777 Shell* new_shell = CreateBrowser();
5778 EXPECT_TRUE(NavigateToURL(new_shell, foo_url));
5779 FrameTreeNode* new_root =
5780 static_cast<WebContentsImpl*>(new_shell->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:555781 ->GetPrimaryFrameTree()
5782 .root();
Alex Moshchuk8e5c1952019-01-15 03:39:505783 FrameTreeNode* new_child = new_root->child_at(0);
5784
5785 EXPECT_EQ(new_root->current_frame_host()->GetProcess(),
5786 root->current_frame_host()->GetProcess());
5787 EXPECT_NE(new_root->current_frame_host()->GetSiteInstance(),
5788 root->current_frame_host()->GetSiteInstance());
5789 EXPECT_FALSE(
5790 new_root->current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance(
5791 root->current_frame_host()->GetSiteInstance()));
5792
5793 // Navigate iframe in the second window to bar.com, and check that it becomes
5794 // an OOPIF in its own process.
5795 NavigateIframeToURL(new_shell->web_contents(), "test_iframe", bar_url);
5796 EXPECT_EQ(new_child->current_url(), bar_url);
5797
5798 EXPECT_NE(new_child->current_frame_host()->GetProcess(),
5799 new_root->current_frame_host()->GetProcess());
5800 EXPECT_NE(new_child->current_frame_host()->GetProcess(),
5801 root->current_frame_host()->GetProcess());
5802 EXPECT_NE(new_child->current_frame_host()->GetProcess(),
5803 child->current_frame_host()->GetProcess());
5804
5805 EXPECT_NE(new_child->current_frame_host()->GetSiteInstance(),
5806 new_root->current_frame_host()->GetSiteInstance());
5807 EXPECT_NE(new_child->current_frame_host()->GetSiteInstance(),
5808 child->current_frame_host()->GetSiteInstance());
5809
Alex Moshchuk147505892020-11-10 05:33:135810 // The old foo.com process should still be able to access bar.com data,
5811 // since it isn't locked to a specific site.
Emily Andrewsd15fd762024-12-10 20:41:545812 int old_process_id =
5813 root->current_frame_host()->GetProcess()->GetDeprecatedID();
Lukasz Anforowiczd0d8cdb2021-01-27 22:20:475814 EXPECT_TRUE(policy->CanAccessDataForOrigin(old_process_id,
5815 url::Origin::Create(bar_url)));
Alex Moshchuk147505892020-11-10 05:33:135816
5817 // In particular, make sure the bar.com iframe in the old foo.com process can
5818 // still access bar.com cookies.
Avi Drissmanc91bd8e2021-04-19 23:58:445819 EXPECT_TRUE(
5820 ExecJs(child, "document.cookie = 'foo=bar;SameSite=None;Secure';"));
Alex Moshchuk8e5c1952019-01-15 03:39:505821 EXPECT_EQ("foo=bar", EvalJs(child, "document.cookie"));
Alex Moshchuk147505892020-11-10 05:33:135822
W. James MacLean89307252020-11-11 00:16:445823 // Make sure the BrowsingInstanceId is cleaned up immediately.
5824 policy->SetBrowsingInstanceCleanupDelayForTesting(0);
5825
Alex Moshchuk147505892020-11-10 05:33:135826 // Now close the first window. This destroys the first BrowsingInstance and
5827 // leaves only the newer BrowsingInstance (with a foo.com main frame) in the
5828 // old process.
5829 shell()->Close();
5830
W. James MacLean89307252020-11-11 00:16:445831 // Now that the process only contains a BrowsingInstance where bar.com is
5832 // considered isolated and cannot reuse the old process, it should lose access
5833 // to bar.com's data due to citadel enforcement in CanAccessDataForOrigin.
Alex Moshchuk989aa22c2024-11-06 02:09:075834 //
5835 // However, note that the access won't be revoked if
5836 // ChildProcessSecurityPolicy uses new security enforcements based on lists of
5837 // committed origins, since committed origins are currently never revoked from
5838 // a process.
5839 // TODO(crbug.com/40148776): This may need to be revisited in the future,
5840 // e.g., by marking newly isolated origins as needing revocation when their
5841 // last instance goes away from a process.
5842 if (base::FeatureList::IsEnabled(features::kCommittedOriginEnforcements)) {
5843 EXPECT_TRUE(policy->CanAccessDataForOrigin(old_process_id,
5844 url::Origin::Create(bar_url)));
5845 } else {
5846 EXPECT_FALSE(policy->CanAccessDataForOrigin(old_process_id,
5847 url::Origin::Create(bar_url)));
5848 }
Nick Carter855bc492018-03-10 00:44:575849}
5850
Alex Moshchuk58225c82019-04-18 00:45:015851// Verify that a process locked to foo.com is not reused for a navigation to
5852// foo.com that does not require a dedicated process. See
5853// https://crbug.com/950453.
5854IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest,
5855 LockedProcessNotReusedForNonisolatedSameSiteNavigation) {
5856 // This test is designed to run without strict site isolation.
Jagadesh P1c8699c22023-11-13 14:09:375857 if (AreAllSitesIsolatedForTesting()) {
Alex Moshchuk58225c82019-04-18 00:45:015858 return;
Jagadesh P1c8699c22023-11-13 14:09:375859 }
Alex Moshchuk58225c82019-04-18 00:45:015860
5861 // Set the process limit to 1.
5862 RenderProcessHost::SetMaxRendererProcessCount(1);
5863
5864 // Start on a non-isolated foo.com URL.
5865 GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
5866 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
5867
5868 // Navigate to a different isolated origin and wait for the original foo.com
5869 // process to shut down. Note that the foo.com SiteInstance will stick
5870 // around in session history.
5871 RenderProcessHostWatcher foo_process_observer(
Dave Tapuska327c06c92022-06-13 20:31:515872 web_contents()->GetPrimaryMainFrame()->GetProcess(),
Alex Moshchuk58225c82019-04-18 00:45:015873 RenderProcessHostWatcher::WATCH_FOR_HOST_DESTRUCTION);
Sreeja Kamishettyce8d5942020-08-19 11:25:515874
5875 // Disable the BackForwardCache to ensure the old process is going to be
5876 // released.
5877 DisableBackForwardCacheForTesting(web_contents(),
Rakina Zata Amni30af7062022-01-19 23:46:365878 BackForwardCache::TEST_REQUIRES_NO_CACHING);
Sreeja Kamishettyce8d5942020-08-19 11:25:515879
Alex Moshchuk58225c82019-04-18 00:45:015880 GURL isolated_bar_url(
5881 embedded_test_server()->GetURL("isolated.bar.com", "/title1.html"));
5882 EXPECT_TRUE(NavigateToURL(shell(), isolated_bar_url));
5883 foo_process_observer.Wait();
5884 EXPECT_TRUE(foo_process_observer.did_exit_normally());
5885
5886 // Start isolating foo.com.
5887 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
Alex Moshchukef8c2562021-03-12 06:37:455888 policy->AddFutureIsolatedOrigins({url::Origin::Create(foo_url)},
5889 IsolatedOriginSource::TEST);
Alex Moshchuk58225c82019-04-18 00:45:015890
5891 // Create a new window, forcing a new BrowsingInstance, and navigate it to
5892 // foo.com, which will spin up a process locked to foo.com.
5893 Shell* new_shell = CreateBrowser();
5894 EXPECT_TRUE(NavigateToURL(new_shell, foo_url));
5895 RenderProcessHost* new_process =
Dave Tapuska327c06c92022-06-13 20:31:515896 new_shell->web_contents()->GetPrimaryMainFrame()->GetProcess();
W. James MacLeane84fa112020-07-14 17:25:545897 EXPECT_EQ(ProcessLockFromUrl("http://foo.com"),
Sharon Yang57481e52021-12-01 00:05:225898 new_process->GetProcessLock());
Alex Moshchuk58225c82019-04-18 00:45:015899
5900 // Go to foo.com in the older first tab, where foo.com does not require a
5901 // dedicated process. Ensure that the existing locked foo.com process is
W. James MacLeane84fa112020-07-14 17:25:545902 // *not* reused in that case (if that were the case, LockProcessIfNeeded
Alex Moshchuk58225c82019-04-18 00:45:015903 // would trigger a CHECK here). Using a history navigation here ensures that
5904 // the SiteInstance (from session history) will have a foo.com site URL,
5905 // rather than a default site URL, since this case isn't yet handled by the
5906 // default SiteInstance (see crbug.com/787576).
5907 TestNavigationObserver observer(web_contents());
5908 web_contents()->GetController().GoBack();
5909 observer.Wait();
Dave Tapuska327c06c92022-06-13 20:31:515910 EXPECT_NE(web_contents()->GetPrimaryMainFrame()->GetProcess(), new_process);
Alex Moshchuk58225c82019-04-18 00:45:015911}
5912
Alex Moshchuk99b795422019-03-07 00:27:325913// Checks that isolated origins can be added only for a specific profile,
5914// and that they don't apply to other profiles.
5915IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest, PerProfileIsolation) {
5916 // This test is designed to run without strict site isolation.
Jagadesh P1c8699c22023-11-13 14:09:375917 if (AreAllSitesIsolatedForTesting()) {
Alex Moshchuk99b795422019-03-07 00:27:325918 return;
Jagadesh P1c8699c22023-11-13 14:09:375919 }
Alex Moshchuk99b795422019-03-07 00:27:325920
5921 // Create a browser in a different profile.
5922 BrowserContext* main_context = shell()->web_contents()->GetBrowserContext();
5923 Shell* other_shell = CreateOffTheRecordBrowser();
5924 BrowserContext* other_context =
5925 other_shell->web_contents()->GetBrowserContext();
5926 ASSERT_NE(main_context, other_context);
5927
5928 // Start on bar.com in both browsers.
5929 GURL bar_url(embedded_test_server()->GetURL("bar.com", "/title1.html"));
5930 EXPECT_TRUE(NavigateToURL(shell(), bar_url));
5931 EXPECT_TRUE(NavigateToURL(other_shell, bar_url));
5932
Ari Chivukula5350aad92021-08-10 02:42:245933 // Start isolating foo.com in `other_context` only.
Alex Moshchuk99b795422019-03-07 00:27:325934 GURL foo_url(
5935 embedded_test_server()->GetURL("foo.com", "/page_with_iframe.html"));
5936 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
Alex Moshchukef8c2562021-03-12 06:37:455937 policy->AddFutureIsolatedOrigins({url::Origin::Create(foo_url)},
5938 IsolatedOriginSource::TEST, other_context);
Alex Moshchuk99b795422019-03-07 00:27:325939
Ari Chivukula5350aad92021-08-10 02:42:245940 // Verify that foo.com is indeed isolated in `other_shell`, by navigating to
Alex Moshchuk99b795422019-03-07 00:27:325941 // it in a new BrowsingInstance and checking that a bar.com subframe becomes
5942 // an OOPIF.
5943 EXPECT_TRUE(NavigateToURL(other_shell, foo_url));
5944 WebContentsImpl* other_contents =
5945 static_cast<WebContentsImpl*>(other_shell->web_contents());
5946 NavigateIframeToURL(other_contents, "test_iframe", bar_url);
Carlos Caballero15caeeb2021-10-27 09:57:555947 FrameTreeNode* root = other_contents->GetPrimaryFrameTree().root();
Alex Moshchuk99b795422019-03-07 00:27:325948 FrameTreeNode* child = root->child_at(0);
5949 EXPECT_EQ(child->current_url(), bar_url);
5950 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
5951 child->current_frame_host()->GetSiteInstance());
5952 EXPECT_NE(root->current_frame_host()->GetProcess(),
5953 child->current_frame_host()->GetProcess());
5954
5955 // Verify that foo.com is *not* isolated in the regular shell, due to a
5956 // different profile.
5957 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
5958 NavigateIframeToURL(web_contents(), "test_iframe", bar_url);
Carlos Caballero15caeeb2021-10-27 09:57:555959 root = web_contents()->GetPrimaryFrameTree().root();
Alex Moshchuk99b795422019-03-07 00:27:325960 child = root->child_at(0);
5961 EXPECT_EQ(child->current_url(), bar_url);
Aaron Colwell5fb878042020-12-17 19:48:445962 if (AreStrictSiteInstancesEnabled()) {
5963 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
5964 child->current_frame_host()->GetSiteInstance());
5965 } else {
5966 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
5967 child->current_frame_host()->GetSiteInstance());
5968 }
Alex Moshchuk99b795422019-03-07 00:27:325969 EXPECT_EQ(root->current_frame_host()->GetProcess(),
5970 child->current_frame_host()->GetProcess());
5971}
5972
Alex Moshchukdc3560cb2019-04-17 22:05:075973// Check that a dynamically added isolated origin can take effect on the next
5974// main frame navigation by forcing a BrowsingInstance swap, in the case that
5975// there are no script references to the frame being navigated.
5976IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest, ForceBrowsingInstanceSwap) {
5977 // This test is designed to run without strict site isolation.
Jagadesh P1c8699c22023-11-13 14:09:375978 if (AreAllSitesIsolatedForTesting()) {
Alex Moshchukdc3560cb2019-04-17 22:05:075979 return;
Jagadesh P1c8699c22023-11-13 14:09:375980 }
Alex Moshchukdc3560cb2019-04-17 22:05:075981
5982 // Navigate to a non-isolated page with a cross-site iframe. The frame
5983 // shouldn't be in an OOPIF.
5984 GURL foo_url(embedded_test_server()->GetURL(
5985 "foo.com", "/cross_site_iframe_factory.html?foo.com(bar.com)"));
5986 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
Carlos Caballero15caeeb2021-10-27 09:57:555987 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Alex Moshchukdc3560cb2019-04-17 22:05:075988 FrameTreeNode* child = root->child_at(0);
5989 scoped_refptr<SiteInstance> first_instance =
5990 root->current_frame_host()->GetSiteInstance();
Aaron Colwell5fb878042020-12-17 19:48:445991
5992 if (AreStrictSiteInstancesEnabled()) {
5993 EXPECT_NE(first_instance, child->current_frame_host()->GetSiteInstance());
5994 } else {
5995 EXPECT_EQ(first_instance, child->current_frame_host()->GetSiteInstance());
5996 }
Alex Moshchukdc3560cb2019-04-17 22:05:075997 EXPECT_EQ(root->current_frame_host()->GetProcess(),
5998 child->current_frame_host()->GetProcess());
5999 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
Sharon Yang57481e52021-12-01 00:05:226000 EXPECT_TRUE(first_instance->GetProcess()->GetProcessLock().allows_any_site());
Alex Moshchukdc3560cb2019-04-17 22:05:076001
6002 // Start isolating foo.com.
6003 BrowserContext* context = shell()->web_contents()->GetBrowserContext();
Alex Moshchukef8c2562021-03-12 06:37:456004 policy->AddFutureIsolatedOrigins({url::Origin::Create(foo_url)},
6005 IsolatedOriginSource::TEST, context);
Alex Moshchukdc3560cb2019-04-17 22:05:076006
6007 // Try navigating to another foo URL.
6008 GURL foo2_url(embedded_test_server()->GetURL(
6009 "foo.com", "/cross_site_iframe_factory.html?foo.com(baz.com)"));
6010 EXPECT_TRUE(NavigateToURL(shell(), foo2_url));
6011
6012 // Verify that this navigation ended up in a dedicated process, and that we
6013 // swapped BrowsingInstances in the process.
6014 scoped_refptr<SiteInstance> second_instance =
6015 root->current_frame_host()->GetSiteInstance();
6016 EXPECT_NE(first_instance, second_instance);
6017 EXPECT_FALSE(first_instance->IsRelatedSiteInstance(second_instance.get()));
Jiacheng Guocd621762025-06-17 01:14:326018 EXPECT_NE(first_instance->GetOrCreateProcessForTesting(),
Jiacheng Guo99c6fb42025-02-03 08:11:556019 second_instance->GetProcess());
W. James MacLeane84fa112020-07-14 17:25:546020 EXPECT_EQ(ProcessLockFromUrl("http://foo.com"),
Sharon Yang57481e52021-12-01 00:05:226021 second_instance->GetProcess()->GetProcessLock());
Alex Moshchukdc3560cb2019-04-17 22:05:076022
6023 // The frame on that page should now be an OOPIF.
6024 child = root->child_at(0);
6025 EXPECT_NE(second_instance, child->current_frame_host()->GetSiteInstance());
6026 EXPECT_NE(root->current_frame_host()->GetProcess(),
6027 child->current_frame_host()->GetProcess());
6028}
6029
6030// Same as the test above, but using a renderer-initiated navigation. Check
6031// that a dynamically added isolated origin can take effect on the next main
6032// frame navigation by forcing a BrowsingInstance swap, in the case that there
6033// are no script references to the frame being navigated.
6034IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest,
6035 ForceBrowsingInstanceSwap_RendererInitiated) {
6036 // This test is designed to run without strict site isolation.
Jagadesh P1c8699c22023-11-13 14:09:376037 if (AreAllSitesIsolatedForTesting()) {
Alex Moshchukdc3560cb2019-04-17 22:05:076038 return;
Jagadesh P1c8699c22023-11-13 14:09:376039 }
Alex Moshchukdc3560cb2019-04-17 22:05:076040
6041 // Navigate to a foo.com page.
6042 GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
6043 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
Carlos Caballero15caeeb2021-10-27 09:57:556044 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Alex Moshchukdc3560cb2019-04-17 22:05:076045 scoped_refptr<SiteInstance> first_instance =
6046 root->current_frame_host()->GetSiteInstance();
6047 EXPECT_FALSE(first_instance->RequiresDedicatedProcess());
6048 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
Sharon Yang57481e52021-12-01 00:05:226049 EXPECT_TRUE(first_instance->GetProcess()->GetProcessLock().allows_any_site());
Alex Moshchukdc3560cb2019-04-17 22:05:076050
6051 // Set a sessionStorage value, to sanity check that foo.com's session storage
6052 // will still be accessible after the BrowsingInstance swap.
6053 EXPECT_TRUE(ExecJs(root, "window.sessionStorage['foo'] = 'bar';"));
6054
6055 // Start isolating foo.com.
6056 BrowserContext* context = shell()->web_contents()->GetBrowserContext();
Alex Moshchukef8c2562021-03-12 06:37:456057 policy->AddFutureIsolatedOrigins({url::Origin::Create(foo_url)},
6058 IsolatedOriginSource::TEST, context);
Alex Moshchukdc3560cb2019-04-17 22:05:076059
6060 // Do a renderer-initiated navigation to another foo URL.
6061 GURL foo2_url(embedded_test_server()->GetURL(
6062 "foo.com", "/cross_site_iframe_factory.html?foo.com(baz.com)"));
6063 EXPECT_TRUE(NavigateToURLFromRenderer(shell(), foo2_url));
6064
6065 // Verify that this navigation ended up in a dedicated process, and that we
6066 // swapped BrowsingInstances in the process.
6067 scoped_refptr<SiteInstance> second_instance =
6068 root->current_frame_host()->GetSiteInstance();
6069 EXPECT_NE(first_instance, second_instance);
6070 EXPECT_FALSE(first_instance->IsRelatedSiteInstance(second_instance.get()));
Jiacheng Guocd621762025-06-17 01:14:326071 EXPECT_NE(first_instance->GetOrCreateProcessForTesting(),
Jiacheng Guo99c6fb42025-02-03 08:11:556072 second_instance->GetProcess());
W. James MacLeane84fa112020-07-14 17:25:546073 EXPECT_EQ(ProcessLockFromUrl("http://foo.com"),
Sharon Yang57481e52021-12-01 00:05:226074 second_instance->GetProcess()->GetProcessLock());
Alex Moshchukdc3560cb2019-04-17 22:05:076075
6076 // The frame on that page should be an OOPIF.
6077 FrameTreeNode* child = root->child_at(0);
6078 EXPECT_NE(second_instance, child->current_frame_host()->GetSiteInstance());
6079 EXPECT_NE(root->current_frame_host()->GetProcess(),
6080 child->current_frame_host()->GetProcess());
6081
6082 // Verify that the isolated foo.com page can still access session storage set
6083 // by the previous foo.com page.
6084 EXPECT_EQ("bar", EvalJs(root, "window.sessionStorage['foo']"));
6085}
6086
6087IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest,
6088 DontForceBrowsingInstanceSwapWhenScriptReferencesExist) {
6089 // This test is designed to run without strict site isolation.
Jagadesh P1c8699c22023-11-13 14:09:376090 if (AreAllSitesIsolatedForTesting()) {
Alex Moshchukdc3560cb2019-04-17 22:05:076091 return;
Jagadesh P1c8699c22023-11-13 14:09:376092 }
Alex Moshchukdc3560cb2019-04-17 22:05:076093
6094 // Navigate to a page that won't be in a dedicated process.
6095 GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
6096 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
Carlos Caballero15caeeb2021-10-27 09:57:556097 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Alex Moshchukdc3560cb2019-04-17 22:05:076098 scoped_refptr<SiteInstance> first_instance =
6099 root->current_frame_host()->GetSiteInstance();
6100 EXPECT_FALSE(first_instance->RequiresDedicatedProcess());
6101
6102 // Start isolating foo.com.
6103 BrowserContext* context = shell()->web_contents()->GetBrowserContext();
6104 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
Alex Moshchukef8c2562021-03-12 06:37:456105 policy->AddFutureIsolatedOrigins({url::Origin::Create(foo_url)},
6106 IsolatedOriginSource::TEST, context);
Alex Moshchukdc3560cb2019-04-17 22:05:076107
6108 // Open a popup.
6109 GURL popup_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
6110 OpenPopup(shell(), popup_url, "");
6111
6112 // Try navigating the main frame to another foo URL.
6113 GURL foo2_url(embedded_test_server()->GetURL("foo.com", "/title2.html"));
6114 EXPECT_TRUE(NavigateToURLFromRenderer(shell(), foo2_url));
6115
6116 // This navigation should not end up in a dedicated process. The popup
6117 // should prevent the BrowsingInstance swap heuristic from applying, since it
6118 // should still be able to communicate with the opener after the navigation.
6119 EXPECT_EQ(first_instance, root->current_frame_host()->GetSiteInstance());
6120 EXPECT_FALSE(first_instance->RequiresDedicatedProcess());
Sharon Yang57481e52021-12-01 00:05:226121 EXPECT_TRUE(first_instance->GetProcess()->GetProcessLock().allows_any_site());
Alex Moshchukdc3560cb2019-04-17 22:05:076122}
6123
6124// This test ensures that when a page becomes isolated in the middle of
6125// creating and navigating a new window, the new window prevents a
6126// BrowsingInstance swap.
6127IN_PROC_BROWSER_TEST_F(
6128 DynamicIsolatedOriginTest,
6129 DontForceBrowsingInstanceSwapWithPendingNavigationInNewWindow) {
6130 // This test is designed to run without strict site isolation.
Jagadesh P1c8699c22023-11-13 14:09:376131 if (AreAllSitesIsolatedForTesting()) {
Alex Moshchukdc3560cb2019-04-17 22:05:076132 return;
Jagadesh P1c8699c22023-11-13 14:09:376133 }
Alex Moshchukdc3560cb2019-04-17 22:05:076134
6135 // Navigate to a page that won't be in a dedicated process.
6136 GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
6137 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
Carlos Caballero15caeeb2021-10-27 09:57:556138 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Alex Moshchukdc3560cb2019-04-17 22:05:076139 scoped_refptr<SiteInstance> first_instance =
6140 root->current_frame_host()->GetSiteInstance();
6141 EXPECT_FALSE(first_instance->RequiresDedicatedProcess());
6142
6143 // Open and start navigating a popup to a URL that never finishes loading.
6144 GURL popup_url(embedded_test_server()->GetURL("a.com", "/hung"));
Avi Drissmanc91bd8e2021-04-19 23:58:446145 EXPECT_TRUE(ExecJs(root, JsReplace("window.open($1);", popup_url)));
Alex Moshchukdc3560cb2019-04-17 22:05:076146
6147 // Start isolating foo.com.
6148 BrowserContext* context = shell()->web_contents()->GetBrowserContext();
6149 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
Alex Moshchukef8c2562021-03-12 06:37:456150 policy->AddFutureIsolatedOrigins({url::Origin::Create(foo_url)},
6151 IsolatedOriginSource::TEST, context);
Alex Moshchukdc3560cb2019-04-17 22:05:076152
6153 // Navigate the main frame to another foo URL.
6154 GURL foo2_url(embedded_test_server()->GetURL("foo.com", "/title2.html"));
6155 EXPECT_TRUE(NavigateToURLFromRenderer(shell(), foo2_url));
6156
6157 // This navigation should not end up in a dedicated process. The pending
6158 // navigation in the popup should prevent the BrowsingInstance swap heuristic
6159 // from applying, since it should still be able to communicate with the
6160 // opener after the navigation.
6161 EXPECT_EQ(first_instance, root->current_frame_host()->GetSiteInstance());
6162 EXPECT_FALSE(first_instance->RequiresDedicatedProcess());
Sharon Yang57481e52021-12-01 00:05:226163 EXPECT_TRUE(first_instance->GetProcess()->GetProcessLock().allows_any_site());
Alex Moshchukdc3560cb2019-04-17 22:05:076164}
6165
Sharon Yang1a7a63702025-05-23 01:14:556166class IsolatedOriginTestWithDefaultSiteInstanceGroups
6167 : public IsolatedOriginTest,
6168 public ::testing::WithParamInterface<bool> {
6169 public:
Nate Chapin71da03c2019-02-05 01:21:416170 void SetUpCommandLine(base::CommandLine* command_line) override {
6171 IsolatedOriginTest::SetUpCommandLine(command_line);
6172 command_line->AppendSwitch(switches::kDisableSiteIsolation);
Sharon Yang6a61229f2025-02-11 17:56:546173 command_line->RemoveSwitch(switches::kSitePerProcess);
Sharon Yang1a7a63702025-05-23 01:14:556174
6175 if (IsDefaultSiteInstanceGroupEnabled()) {
6176 feature_list_.InitAndEnableFeature(features::kDefaultSiteInstanceGroups);
6177 } else {
6178 feature_list_.InitAndDisableFeature(features::kDefaultSiteInstanceGroups);
6179 }
Nate Chapin71da03c2019-02-05 01:21:416180 }
Sharon Yang1a7a63702025-05-23 01:14:556181
6182 static std::string DescribeParams(
6183 const testing::TestParamInfo<ParamType>& info) {
6184 return info.param ? "UseDefaultSiteInstanceGroups"
6185 : "UseDefaultSiteInstances";
6186 }
6187
6188 private:
6189 bool IsDefaultSiteInstanceGroupEnabled() const { return GetParam(); }
6190
6191 base::test::ScopedFeatureList feature_list_;
Nate Chapin71da03c2019-02-05 01:21:416192};
6193
Sharon Yang1a7a63702025-05-23 01:14:556194IN_PROC_BROWSER_TEST_P(IsolatedOriginTestWithDefaultSiteInstanceGroups,
6195 NonIsolatedFramesCanShareDefaultProcess) {
Nate Chapin71da03c2019-02-05 01:21:416196 GURL top_url(
6197 embedded_test_server()->GetURL("/frame_tree/page_with_two_frames.html"));
6198 ASSERT_FALSE(IsIsolatedOrigin(url::Origin::Create(top_url)));
6199 EXPECT_TRUE(NavigateToURL(shell(), top_url));
6200
Carlos Caballero15caeeb2021-10-27 09:57:556201 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Nate Chapin71da03c2019-02-05 01:21:416202 FrameTreeNode* child1 = root->child_at(0);
6203 FrameTreeNode* child2 = root->child_at(1);
6204
6205 GURL bar_url(embedded_test_server()->GetURL("www.bar.com", "/title3.html"));
6206 ASSERT_FALSE(IsIsolatedOrigin(url::Origin::Create(bar_url)));
6207 {
6208 TestFrameNavigationObserver observer(child1);
6209 NavigationHandleObserver handle_observer(web_contents(), bar_url);
Avi Drissmanc91bd8e2021-04-19 23:58:446210 EXPECT_TRUE(ExecJs(child1, "location.href = '" + bar_url.spec() + "';"));
Nate Chapin71da03c2019-02-05 01:21:416211 observer.Wait();
6212 }
6213
6214 GURL baz_url(embedded_test_server()->GetURL("www.baz.com", "/title3.html"));
6215 ASSERT_FALSE(IsIsolatedOrigin(url::Origin::Create(baz_url)));
6216 {
6217 TestFrameNavigationObserver observer(child2);
6218 NavigationHandleObserver handle_observer(web_contents(), baz_url);
Avi Drissmanc91bd8e2021-04-19 23:58:446219 EXPECT_TRUE(ExecJs(child2, "location.href = '" + baz_url.spec() + "';"));
Nate Chapin71da03c2019-02-05 01:21:416220 observer.Wait();
6221 }
6222
Sharon Yang1a7a63702025-05-23 01:14:556223 if (ShouldUseDefaultSiteInstanceGroup()) {
6224 // All 3 frames are different sites, so each should have its own
6225 // SiteInstance.
6226 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
6227 child1->current_frame_host()->GetSiteInstance());
6228 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
6229 child2->current_frame_host()->GetSiteInstance());
6230 EXPECT_NE(child1->current_frame_host()->GetSiteInstance(),
6231 child2->current_frame_host()->GetSiteInstance());
6232
6233 // All 3 sites should share the default SiteInstanceGroup and be in the same
6234 // process, so no proxies are needed.
6235 EXPECT_EQ(
6236 " Site A\n"
6237 " |--Site B\n"
6238 " +--Site C\n"
6239 "Where A = http://127.0.0.1/\n"
6240 " B = http://bar.com/\n"
6241 " C = http://baz.com/",
6242 DepictFrameTree(*root));
6243 } else {
6244 // All 3 frames are in the default SiteInstance.
6245 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
6246 child1->current_frame_host()->GetSiteInstance());
6247 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
6248 child2->current_frame_host()->GetSiteInstance());
6249 EXPECT_EQ(child1->current_frame_host()->GetSiteInstance(),
6250 child2->current_frame_host()->GetSiteInstance());
6251
6252 EXPECT_EQ(
6253 " Site A\n"
6254 " |--Site A\n"
6255 " +--Site A\n"
6256 "Where A = http://unisolated.invalid/",
6257 DepictFrameTree(*root));
6258 }
Nate Chapin71da03c2019-02-05 01:21:416259
6260 // But none are isolated, so all should share the default process for their
6261 // BrowsingInstance.
6262 RenderProcessHost* host = root->current_frame_host()->GetProcess();
6263 EXPECT_EQ(host, child1->current_frame_host()->GetProcess());
6264 EXPECT_EQ(host, child2->current_frame_host()->GetProcess());
Sharon Yang57481e52021-12-01 00:05:226265 EXPECT_TRUE(host->GetProcessLock().allows_any_site());
Nate Chapin71da03c2019-02-05 01:21:416266}
6267
6268// Creates a non-isolated main frame with an isolated child and non-isolated
6269// grandchild. With strict site isolation disabled and
Sharon Yang1a7a63702025-05-23 01:14:556270// default SiteInstanceGroups enabled, the main frame and the grandchild should
6271// be in the default SiteInstanceGroup with different SiteInstances.
6272IN_PROC_BROWSER_TEST_P(IsolatedOriginTestWithDefaultSiteInstanceGroups,
6273 IsolatedChildWithNonIsolatedGrandchild) {
Nate Chapin71da03c2019-02-05 01:21:416274 GURL top_url(
6275 embedded_test_server()->GetURL("www.foo.com", "/page_with_iframe.html"));
6276 ASSERT_FALSE(IsIsolatedOrigin(url::Origin::Create(top_url)));
6277 EXPECT_TRUE(NavigateToURL(shell(), top_url));
6278
6279 GURL isolated_url(embedded_test_server()->GetURL("isolated.foo.com",
6280 "/page_with_iframe.html"));
6281 ASSERT_TRUE(IsIsolatedOrigin(url::Origin::Create(isolated_url)));
6282
Carlos Caballero15caeeb2021-10-27 09:57:556283 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Nate Chapin71da03c2019-02-05 01:21:416284 FrameTreeNode* child = root->child_at(0);
6285
6286 NavigateIframeToURL(web_contents(), "test_iframe", isolated_url);
6287 EXPECT_EQ(child->current_url(), isolated_url);
6288
6289 // Verify that the child frame is an OOPIF with a different SiteInstance.
6290 EXPECT_NE(web_contents()->GetSiteInstance(),
6291 child->current_frame_host()->GetSiteInstance());
6292 EXPECT_TRUE(child->current_frame_host()->IsCrossProcessSubframe());
6293 EXPECT_EQ(GURL("http://isolated.foo.com/"),
6294 child->current_frame_host()->GetSiteInstance()->GetSiteURL());
6295
6296 // Verify that the isolated frame's subframe (which starts out at a relative
6297 // path) is kept in the isolated parent's SiteInstance.
6298 FrameTreeNode* grandchild = child->child_at(0);
6299 EXPECT_EQ(child->current_frame_host()->GetSiteInstance(),
6300 grandchild->current_frame_host()->GetSiteInstance());
6301
6302 // Navigating the grandchild to www.bar.com should put it into the top
6303 // frame's process, but not its SiteInstance.
6304 GURL non_isolated_url(
6305 embedded_test_server()->GetURL("www.bar.com", "/title3.html"));
6306 ASSERT_FALSE(IsIsolatedOrigin(url::Origin::Create(non_isolated_url)));
6307 TestFrameNavigationObserver observer(grandchild);
Avi Drissmanc91bd8e2021-04-19 23:58:446308 EXPECT_TRUE(
6309 ExecJs(grandchild, "location.href = '" + non_isolated_url.spec() + "';"));
Nate Chapin71da03c2019-02-05 01:21:416310 observer.Wait();
6311 EXPECT_EQ(non_isolated_url, grandchild->current_url());
6312
Nate Chapin71da03c2019-02-05 01:21:416313 EXPECT_NE(child->current_frame_host()->GetSiteInstance(),
6314 grandchild->current_frame_host()->GetSiteInstance());
6315 EXPECT_EQ(root->current_frame_host()->GetProcess(),
6316 grandchild->current_frame_host()->GetProcess());
Sharon Yang1a7a63702025-05-23 01:14:556317 if (ShouldUseDefaultSiteInstanceGroup()) {
6318 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
6319 grandchild->current_frame_host()->GetSiteInstance());
6320 EXPECT_EQ(
6321 " Site A ------------ proxies for B\n"
6322 " +--Site B ------- proxies for {A,C}\n"
6323 " +--Site C -- proxies for B\n"
6324 "Where A = http://foo.com/\n"
6325 " B = http://isolated.foo.com/\n"
6326 " C = http://bar.com/",
6327 DepictFrameTree(*root));
6328 } else {
6329 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
6330 grandchild->current_frame_host()->GetSiteInstance());
6331 EXPECT_EQ(
6332 " Site A ------------ proxies for B\n"
6333 " +--Site B ------- proxies for A\n"
6334 " +--Site A -- proxies for B\n"
6335 "Where A = http://unisolated.invalid/\n"
6336 " B = http://isolated.foo.com/",
6337 DepictFrameTree(*root));
6338 }
Nate Chapin71da03c2019-02-05 01:21:416339}
6340
6341// Navigate a frame into and out of an isolated origin. This should not
Sharon Yang1a7a63702025-05-23 01:14:556342// confuse BrowsingInstance into holding onto a stale default SiteInstance or
6343// default SiteInstanceGroup
6344IN_PROC_BROWSER_TEST_P(IsolatedOriginTestWithDefaultSiteInstanceGroups,
6345 SubframeNavigatesOutOfIsolationThenToIsolation) {
6346 // Navigate to an isolated site, with a same-site subframe.
Nate Chapin71da03c2019-02-05 01:21:416347 GURL isolated_url(embedded_test_server()->GetURL("isolated.foo.com",
6348 "/page_with_iframe.html"));
6349 ASSERT_TRUE(IsIsolatedOrigin(url::Origin::Create(isolated_url)));
6350 EXPECT_TRUE(NavigateToURL(shell(), isolated_url));
6351
Carlos Caballero15caeeb2021-10-27 09:57:556352 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Sharon Yang1a7a63702025-05-23 01:14:556353 SiteInstanceImpl* root_instance =
6354 root->current_frame_host()->GetSiteInstance();
Nate Chapin71da03c2019-02-05 01:21:416355 FrameTreeNode* child = root->child_at(0);
Sharon Yang1a7a63702025-05-23 01:14:556356 SiteInstanceImpl* child_instance =
6357 child->current_frame_host()->GetSiteInstance();
6358 EXPECT_EQ(web_contents()->GetSiteInstance(), child_instance);
6359 EXPECT_EQ(root_instance->group(), child_instance->group());
Nate Chapin71da03c2019-02-05 01:21:416360 EXPECT_FALSE(child->current_frame_host()->IsCrossProcessSubframe());
Sharon Yang1a7a63702025-05-23 01:14:556361 EXPECT_FALSE(HasDefaultSiteInstanceOrGroup(child->current_frame_host()));
6362 if (ShouldUseDefaultSiteInstanceGroup()) {
6363 EXPECT_NE(child_instance->group(),
6364 child_instance->DefaultSiteInstanceGroupForBrowsingInstance());
6365 } else {
6366 EXPECT_FALSE(child_instance->IsDefaultSiteInstance());
6367 }
Nate Chapin71da03c2019-02-05 01:21:416368
Sharon Yang1a7a63702025-05-23 01:14:556369 // Navigate the child to a non-isolated page.
Nate Chapin71da03c2019-02-05 01:21:416370 GURL non_isolated_url(
Sharon Yang1a7a63702025-05-23 01:14:556371 embedded_test_server()->GetURL("www.bar.com", "/title3.html"));
Nate Chapin71da03c2019-02-05 01:21:416372 ASSERT_FALSE(IsIsolatedOrigin(url::Origin::Create(non_isolated_url)));
6373 NavigateIframeToURL(web_contents(), "test_iframe", non_isolated_url);
Sharon Yang1a7a63702025-05-23 01:14:556374 child_instance = child->current_frame_host()->GetSiteInstance();
6375
Nate Chapin71da03c2019-02-05 01:21:416376 EXPECT_EQ(child->current_url(), non_isolated_url);
Sharon Yang1a7a63702025-05-23 01:14:556377 EXPECT_TRUE(HasDefaultSiteInstanceOrGroup(child->current_frame_host()));
6378 EXPECT_NE(root_instance->group(), child_instance->group());
6379 // Keep this value for comparing later.
6380 SiteInstanceGroup* first_default_group = child_instance->group();
6381 if (ShouldUseDefaultSiteInstanceGroup()) {
6382 EXPECT_EQ(child_instance->group(),
6383 child_instance->DefaultSiteInstanceGroupForBrowsingInstance());
6384 } else {
6385 EXPECT_TRUE(child_instance->IsDefaultSiteInstance());
6386 }
Nate Chapin71da03c2019-02-05 01:21:416387
Sharon Yang1a7a63702025-05-23 01:14:556388 // Navigate to an isolated page without an iframe. This should cause the
6389 // default SiteInstance/Group to be deleted.
6390 GURL isolated_url2(
6391 embedded_test_server()->GetURL("isolated.foo.com", "/title1.html"));
6392 ASSERT_TRUE(IsIsolatedOrigin(url::Origin::Create(isolated_url2)));
6393 EXPECT_TRUE(NavigateToURL(shell(), isolated_url2));
6394 EXPECT_FALSE(
6395 HasDefaultSiteInstanceOrGroup(web_contents()->GetPrimaryMainFrame()));
Nate Chapin71da03c2019-02-05 01:21:416396
Sharon Yang1a7a63702025-05-23 01:14:556397 // Navigate the main frame to bar.com. We expect bar's SiteInstance to be in
6398 // the default SiteInstance/Group, but a different one from the subframe
6399 // navigation, since that should have been destroyed when we navigated away,
6400 // as it was the last and only SiteInstance in the default SiteInstance/Group.
6401 EXPECT_TRUE(NavigateToURL(shell(), non_isolated_url));
6402 FrameTreeNode* bar = web_contents()->GetPrimaryFrameTree().root();
6403 SiteInstanceImpl* bar_instance = bar->current_frame_host()->GetSiteInstance();
6404 // The SiteInstanceGroup and process from the first time we navigated to
6405 // bar.com should not be reused.
6406 EXPECT_NE(bar_instance->group(), first_default_group);
6407 if (ShouldUseDefaultSiteInstanceGroup()) {
6408 EXPECT_EQ(bar_instance->group(),
6409 bar_instance->DefaultSiteInstanceGroupForBrowsingInstance());
6410 } else {
6411 EXPECT_TRUE(bar_instance->IsDefaultSiteInstance());
6412 }
Nate Chapin71da03c2019-02-05 01:21:416413}
6414
6415// Ensure a popup and its opener can go in the same process, even though
Sharon Yang1a7a63702025-05-23 01:14:556416// they have different SiteInstances with default SiteInstanceGroups enabled.
6417IN_PROC_BROWSER_TEST_P(IsolatedOriginTestWithDefaultSiteInstanceGroups,
6418 NonIsolatedPopup) {
Nate Chapin71da03c2019-02-05 01:21:416419 GURL foo_url(
6420 embedded_test_server()->GetURL("www.foo.com", "/page_with_iframe.html"));
6421 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
Carlos Caballero15caeeb2021-10-27 09:57:556422 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Nate Chapin71da03c2019-02-05 01:21:416423
6424 // Open a blank popup.
6425 ShellAddedObserver new_shell_observer;
Avi Drissmanc91bd8e2021-04-19 23:58:446426 EXPECT_TRUE(ExecJs(root, "window.w = window.open();"));
Nate Chapin71da03c2019-02-05 01:21:416427 Shell* new_shell = new_shell_observer.GetShell();
6428
6429 // Have the opener navigate the popup to a non-isolated origin.
6430 GURL isolated_url(
6431 embedded_test_server()->GetURL("www.bar.com", "/title1.html"));
6432 {
6433 TestNavigationManager manager(new_shell->web_contents(), isolated_url);
Avi Drissmanc91bd8e2021-04-19 23:58:446434 EXPECT_TRUE(ExecJs(
Nate Chapin71da03c2019-02-05 01:21:416435 root, "window.w.location.href = '" + isolated_url.spec() + "';"));
Fergal Daly83bc3cd2023-01-18 00:22:546436 ASSERT_TRUE(manager.WaitForNavigationFinished());
Nate Chapin71da03c2019-02-05 01:21:416437 }
6438
Sharon Yang1a7a63702025-05-23 01:14:556439 // The popup and the opener should not share a SiteInstance in default
6440 // SiteInstanceGroup mode, but should end up in the same process in either
6441 // mode.
Nate Chapin71da03c2019-02-05 01:21:416442 EXPECT_EQ(root->current_frame_host()->GetProcess(),
Dave Tapuska327c06c92022-06-13 20:31:516443 new_shell->web_contents()->GetPrimaryMainFrame()->GetProcess());
Sharon Yang1a7a63702025-05-23 01:14:556444 if (ShouldUseDefaultSiteInstanceGroup()) {
6445 EXPECT_NE(
6446 new_shell->web_contents()->GetPrimaryMainFrame()->GetSiteInstance(),
6447 root->current_frame_host()->GetSiteInstance());
6448 // There should be no proxies between the popup and opener since they share
6449 // a SiteInstanceGroup.
6450 EXPECT_EQ(
6451 " Site A\n"
6452 " +--Site A\n"
6453 "Where A = http://foo.com/",
6454 DepictFrameTree(*root));
6455 EXPECT_EQ(
6456 " Site A\n"
6457 "Where A = http://bar.com/",
6458 DepictFrameTree(
6459 *static_cast<WebContentsImpl*>(new_shell->web_contents())
6460 ->GetPrimaryFrameTree()
6461 .root()));
6462 } else {
6463 EXPECT_EQ(
6464 new_shell->web_contents()->GetPrimaryMainFrame()->GetSiteInstance(),
6465 root->current_frame_host()->GetSiteInstance());
6466 EXPECT_EQ(
6467 " Site A\n"
6468 " +--Site A\n"
6469 "Where A = http://unisolated.invalid/",
6470 DepictFrameTree(*root));
6471 EXPECT_EQ(
6472 " Site A\n"
6473 "Where A = http://unisolated.invalid/",
6474 DepictFrameTree(
6475 *static_cast<WebContentsImpl*>(new_shell->web_contents())
6476 ->GetPrimaryFrameTree()
6477 .root()));
6478 }
Nate Chapin71da03c2019-02-05 01:21:416479}
6480
Sharon Yang6a61229f2025-02-11 17:56:546481// Check that when a cross-site, non-isolated-origin iframe opens a popup,
6482// navigates it to an isolated origin, and then the popup navigates back to its
6483// opener iframe's site, the popup and the opener iframe end up in the same
6484// process and can script each other. See https://crbug.com/796912.
Sharon Yang1a7a63702025-05-23 01:14:556485IN_PROC_BROWSER_TEST_P(IsolatedOriginTestWithDefaultSiteInstanceGroups,
Sharon Yang6a61229f2025-02-11 17:56:546486 PopupNavigatesToIsolatedOriginAndBack) {
6487 // Start on a page with same-site iframe.
6488 GURL foo_url(
6489 embedded_test_server()->GetURL("www.foo.com", "/page_with_iframe.html"));
6490 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
6491 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
6492 FrameTreeNode* child = root->child_at(0);
6493
6494 // Navigate iframe cross-site, but not to an isolated origin. This should
6495 // stay in the main frame's SiteInstance, unless we're in a strict
6496 // SiteInstance mode (including --site-per-process). (Note that the bug for
6497 // which this test is written is exclusive to --isolate-origins and does not
6498 // happen with --site-per-process.)
6499 GURL bar_url(embedded_test_server()->GetURL("bar.com", "/title1.html"));
6500 NavigateIframeToURL(web_contents(), "test_iframe", bar_url);
6501 if (AreStrictSiteInstancesEnabled()) {
6502 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
6503 child->current_frame_host()->GetSiteInstance());
6504 } else {
6505 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
6506 child->current_frame_host()->GetSiteInstance());
6507 }
6508
6509 // Open a blank popup from the iframe.
6510 ShellAddedObserver new_shell_observer;
6511 EXPECT_TRUE(ExecJs(child, "window.w = window.open();"));
6512 Shell* new_shell = new_shell_observer.GetShell();
6513
6514 // Have the opener iframe navigate the popup to an isolated origin.
6515 GURL isolated_url(
6516 embedded_test_server()->GetURL("isolated.foo.com", "/title1.html"));
6517 {
6518 TestNavigationManager manager(new_shell->web_contents(), isolated_url);
6519 EXPECT_TRUE(ExecJs(
6520 child, "window.w.location.href = '" + isolated_url.spec() + "';"));
6521 ASSERT_TRUE(manager.WaitForNavigationFinished());
6522 }
6523
6524 // Simulate the isolated origin in the popup navigating back to bar.com.
6525 GURL bar_url2(embedded_test_server()->GetURL("bar.com", "/title2.html"));
6526 {
6527 TestNavigationManager manager(new_shell->web_contents(), bar_url2);
6528 EXPECT_TRUE(
6529 ExecJs(new_shell, "location.href = '" + bar_url2.spec() + "';"));
6530 ASSERT_TRUE(manager.WaitForNavigationFinished());
6531 }
6532
6533 // Check that the popup ended up in the same SiteInstance as its same-site
6534 // opener iframe.
6535 EXPECT_EQ(new_shell->web_contents()->GetPrimaryMainFrame()->GetSiteInstance(),
6536 child->current_frame_host()->GetSiteInstance());
6537
6538 // Check that the opener iframe can script the popup.
6539 EXPECT_EQ(bar_url2.spec(), EvalJs(child, "window.w.location.href;"));
6540}
6541
Sharon Yang1a7a63702025-05-23 01:14:556542// Make sure a navigation from about:blank in the default SiteInstanceGroup to
6543// an isolated site correctly gets a different process. Also makes sure opening
6544// a popup is same-SiteInstanceGroup.
6545IN_PROC_BROWSER_TEST_P(IsolatedOriginTestWithDefaultSiteInstanceGroups,
6546 InitialEmptyDocumentToIsolatedSite) {
6547 GURL foo_url(embedded_test_server()->GetURL("www.bar.com", "/title1.html"));
6548 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
6549
6550 ShellAddedObserver new_shell1_observer;
6551 EXPECT_TRUE(ExecJs(shell(), "window.w = window.open();"));
6552 Shell* new_shell1 = new_shell1_observer.GetShell();
6553
6554 ShellAddedObserver new_shell2_observer;
6555 EXPECT_TRUE(ExecJs(shell(), "window.w = window.open();"));
6556 Shell* new_shell2 = new_shell2_observer.GetShell();
6557
6558 // Everything should be in the same default SiteInstanceGroup, and thus same
6559 // process, so far.
6560 SiteInstanceImpl* root_site_instance =
6561 web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
6562 SiteInstanceImpl* popup1_site_instance = static_cast<SiteInstanceImpl*>(
6563 new_shell1->web_contents()->GetPrimaryMainFrame()->GetSiteInstance());
6564 SiteInstanceImpl* popup2_site_instance = static_cast<SiteInstanceImpl*>(
6565 new_shell2->web_contents()->GetPrimaryMainFrame()->GetSiteInstance());
6566 EXPECT_EQ(root_site_instance->group(), popup1_site_instance->group());
6567 EXPECT_EQ(popup2_site_instance->group(), popup1_site_instance->group());
6568
6569 // Navigate popup1 to an isolated site.
6570 GURL isolated_url(
6571 embedded_test_server()->GetURL("isolated.foo.com", "/title1.html"));
6572 EXPECT_TRUE(IsIsolatedOrigin(isolated_url));
6573 {
6574 TestNavigationManager manager(new_shell1->web_contents(), isolated_url);
6575 EXPECT_TRUE(
6576 ExecJs(new_shell1, "location.href = '" + isolated_url.spec() + "';"));
6577 ASSERT_TRUE(manager.WaitForNavigationFinished());
6578 }
6579
6580 SiteInstanceImpl* isolated_site_instance = static_cast<SiteInstanceImpl*>(
6581 new_shell1->web_contents()->GetPrimaryMainFrame()->GetSiteInstance());
6582 EXPECT_NE(isolated_site_instance->group(), popup2_site_instance->group());
6583 EXPECT_NE(isolated_site_instance->group(), root_site_instance->group());
6584 EXPECT_NE(isolated_site_instance->group(), popup2_site_instance->group());
6585 EXPECT_EQ(popup2_site_instance->group(), root_site_instance->group());
6586 if (ShouldUseDefaultSiteInstanceGroup()) {
6587 EXPECT_EQ(
6588 popup2_site_instance->group(),
6589 popup2_site_instance->DefaultSiteInstanceGroupForBrowsingInstance());
6590 EXPECT_NE(
6591 isolated_site_instance->group(),
6592 isolated_site_instance->DefaultSiteInstanceGroupForBrowsingInstance());
6593 } else {
6594 EXPECT_TRUE(popup2_site_instance->IsDefaultSiteInstance());
6595 EXPECT_TRUE(root_site_instance->IsDefaultSiteInstance());
6596 EXPECT_FALSE(isolated_site_instance->IsDefaultSiteInstance());
6597 }
6598}
6599
Andrew Stone0a177fe22019-06-26 08:12:046600class WildcardOriginIsolationTest : public IsolatedOriginTestBase {
6601 public:
Sharon Yang034fbb722021-06-23 17:21:326602 WildcardOriginIsolationTest() = default;
6603 ~WildcardOriginIsolationTest() override = default;
6604
6605 WildcardOriginIsolationTest(const WildcardOriginIsolationTest&) = delete;
6606 WildcardOriginIsolationTest& operator=(const WildcardOriginIsolationTest&) =
6607 delete;
Andrew Stone0a177fe22019-06-26 08:12:046608
6609 void SetUpCommandLine(base::CommandLine* command_line) override {
6610 ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
6611
6612 std::string origin_list =
6613 MakeWildcard(embedded_test_server()->GetURL("isolated.foo.com", "/")) +
6614 "," + embedded_test_server()->GetURL("foo.com", "/").spec();
6615
6616 command_line->AppendSwitchASCII(switches::kIsolateOrigins, origin_list);
6617
6618 // This is needed for this test to run properly on platforms where
6619 // --site-per-process isn't the default, such as Android.
6620 IsolateAllSitesForTesting(command_line);
6621 }
6622
6623 void SetUpOnMainThread() override {
6624 host_resolver()->AddRule("*", "127.0.0.1");
6625 embedded_test_server()->StartAcceptingConnections();
6626 }
6627
6628 private:
Andrew Stone404880d2019-07-10 02:23:316629 const char* kAllSubdomainWildcard = "[*.]";
Andrew Stone0a177fe22019-06-26 08:12:046630
6631 // Calling GetURL() on the embedded test server will escape any '*' characters
6632 // into '%2A', so to create a wildcard origin they must be post-processed to
Andrew Stone404880d2019-07-10 02:23:316633 // have the string '[*.]' inserted at the correct point.
Andrew Stone0a177fe22019-06-26 08:12:046634 std::string MakeWildcard(GURL url) {
6635 DCHECK(url.is_valid());
6636 return url.scheme() + url::kStandardSchemeSeparator +
6637 kAllSubdomainWildcard + url.GetContent();
6638 }
Andrew Stone0a177fe22019-06-26 08:12:046639};
6640
6641IN_PROC_BROWSER_TEST_F(WildcardOriginIsolationTest, MainFrameNavigation) {
6642 GURL a_foo_url(embedded_test_server()->GetURL("a.foo.com", "/title1.html"));
6643 GURL b_foo_url(embedded_test_server()->GetURL("b.foo.com", "/title1.html"));
6644 GURL a_isolated_url(
6645 embedded_test_server()->GetURL("a.isolated.foo.com", "/title1.html"));
6646 GURL b_isolated_url(
6647 embedded_test_server()->GetURL("b.isolated.foo.com", "/title1.html"));
6648
6649 EXPECT_TRUE(IsIsolatedOrigin(a_foo_url));
6650 EXPECT_TRUE(IsIsolatedOrigin(b_foo_url));
6651 EXPECT_TRUE(IsIsolatedOrigin(a_isolated_url));
6652 EXPECT_TRUE(IsIsolatedOrigin(b_isolated_url));
6653
6654 // Navigate in the following order, all within the same shell:
6655 // 1. a_foo_url
Rakina Zata Amnid3af5db92020-08-06 06:51:396656 // 2. b_foo_url -- check (1) and (2) have the same pids / instances (*)
Andrew Stone0a177fe22019-06-26 08:12:046657 // 3. a_isolated_url
6658 // 4. b_isolated_url -- check (2), (3) and (4) have distinct pids / instances
6659 // 5. a_foo_url -- check (4) and (5) have distinct pids / instances
Rakina Zata Amnid3af5db92020-08-06 06:51:396660 // 6. b_foo_url -- check (5) and (6) have the same pids / instances (*)
6661 // (*) SiteInstances will be the same unless ProactivelySwapBrowsingInstances
6662 // is enabled for same-site navigations.
Andrew Stone0a177fe22019-06-26 08:12:046663 EXPECT_TRUE(NavigateToURL(shell(), a_foo_url));
Emily Andrewsd15fd762024-12-10 20:41:546664 int a_foo_pid = shell()
6665 ->web_contents()
6666 ->GetPrimaryMainFrame()
6667 ->GetProcess()
6668 ->GetDeprecatedID();
Andrew Stone0a177fe22019-06-26 08:12:046669 scoped_refptr<SiteInstance> a_foo_instance =
Dave Tapuska327c06c92022-06-13 20:31:516670 shell()->web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
Andrew Stone0a177fe22019-06-26 08:12:046671
6672 EXPECT_TRUE(NavigateToURL(shell(), b_foo_url));
Emily Andrewsd15fd762024-12-10 20:41:546673 int b_foo_pid = shell()
6674 ->web_contents()
6675 ->GetPrimaryMainFrame()
6676 ->GetProcess()
6677 ->GetDeprecatedID();
Andrew Stone0a177fe22019-06-26 08:12:046678 scoped_refptr<SiteInstance> b_foo_instance =
Dave Tapuska327c06c92022-06-13 20:31:516679 shell()->web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
Andrew Stone0a177fe22019-06-26 08:12:046680
6681 // Check that hosts in the wildcard subdomain (but not the wildcard subdomain
6682 // itself) have their processes reused between navigation events.
6683 EXPECT_EQ(a_foo_pid, b_foo_pid);
Rakina Zata Amnid3af5db92020-08-06 06:51:396684 if (CanSameSiteMainFrameNavigationsChangeSiteInstances()) {
6685 EXPECT_NE(a_foo_instance, b_foo_instance);
6686 } else {
6687 EXPECT_EQ(a_foo_instance, b_foo_instance);
6688 }
Andrew Stone0a177fe22019-06-26 08:12:046689
6690 EXPECT_TRUE(NavigateToURL(shell(), a_isolated_url));
Emily Andrewsd15fd762024-12-10 20:41:546691 int a_isolated_pid = shell()
6692 ->web_contents()
6693 ->GetPrimaryMainFrame()
6694 ->GetProcess()
6695 ->GetDeprecatedID();
Andrew Stone0a177fe22019-06-26 08:12:046696 scoped_refptr<SiteInstance> a_isolated_instance =
Dave Tapuska327c06c92022-06-13 20:31:516697 shell()->web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
Andrew Stone0a177fe22019-06-26 08:12:046698
6699 EXPECT_TRUE(NavigateToURL(shell(), b_isolated_url));
Emily Andrewsd15fd762024-12-10 20:41:546700 int b_isolated_pid = shell()
6701 ->web_contents()
6702 ->GetPrimaryMainFrame()
6703 ->GetProcess()
6704 ->GetDeprecatedID();
Andrew Stone0a177fe22019-06-26 08:12:046705 scoped_refptr<SiteInstance> b_isolated_instance =
Dave Tapuska327c06c92022-06-13 20:31:516706 shell()->web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
Andrew Stone0a177fe22019-06-26 08:12:046707
6708 // Navigating from a non-wildcard domain to a wildcard domain should result in
6709 // a new process.
6710 EXPECT_NE(b_foo_pid, b_isolated_pid);
6711 EXPECT_NE(b_foo_instance, b_isolated_instance);
6712
6713 // Navigating to another URL within the wildcard domain should always result
6714 // in a new process.
6715 EXPECT_NE(a_isolated_pid, b_isolated_pid);
6716 EXPECT_NE(a_isolated_instance, b_isolated_instance);
6717
6718 EXPECT_TRUE(NavigateToURL(shell(), a_foo_url));
Emily Andrewsd15fd762024-12-10 20:41:546719 a_foo_pid = shell()
6720 ->web_contents()
6721 ->GetPrimaryMainFrame()
6722 ->GetProcess()
6723 ->GetDeprecatedID();
Dave Tapuska327c06c92022-06-13 20:31:516724 a_foo_instance =
6725 shell()->web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
Andrew Stone0a177fe22019-06-26 08:12:046726
6727 EXPECT_TRUE(NavigateToURL(shell(), b_foo_url));
Emily Andrewsd15fd762024-12-10 20:41:546728 b_foo_pid = shell()
6729 ->web_contents()
6730 ->GetPrimaryMainFrame()
6731 ->GetProcess()
6732 ->GetDeprecatedID();
Dave Tapuska327c06c92022-06-13 20:31:516733 b_foo_instance =
6734 shell()->web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
Andrew Stone0a177fe22019-06-26 08:12:046735
6736 // Navigating from the wildcard subdomain to the isolated subdomain should
6737 // produce a new pid.
6738 EXPECT_NE(a_foo_pid, b_isolated_pid);
6739 EXPECT_NE(a_foo_instance, b_isolated_instance);
6740
6741 // Confirm that navigation events in the isolated domain behave the same as
6742 // before visiting the wildcard subdomain.
6743 EXPECT_EQ(a_foo_pid, b_foo_pid);
Rakina Zata Amnid3af5db92020-08-06 06:51:396744 if (CanSameSiteMainFrameNavigationsChangeSiteInstances()) {
6745 EXPECT_NE(a_foo_instance, b_foo_instance);
6746 } else {
6747 EXPECT_EQ(a_foo_instance, b_foo_instance);
6748 }
Andrew Stone0a177fe22019-06-26 08:12:046749}
6750
6751IN_PROC_BROWSER_TEST_F(WildcardOriginIsolationTest, SubFrameNavigation) {
6752 GURL url = embedded_test_server()->GetURL(
6753 "a.foo.com",
6754 "/cross_site_iframe_factory.html?a.foo.com("
6755 "isolated.foo.com,b.foo.com("
6756 "b.isolated.foo.com,a.foo.com,a.isolated.com))");
6757
6758 EXPECT_TRUE(NavigateToURL(shell(), url));
Carlos Caballero15caeeb2021-10-27 09:57:556759 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Andrew Stone0a177fe22019-06-26 08:12:046760
6761 EXPECT_EQ(
6762 " Site A ------------ proxies for B C D\n"
6763 " |--Site B ------- proxies for A C D\n"
6764 " +--Site A ------- proxies for B C D\n"
6765 " |--Site C -- proxies for A B D\n"
6766 " |--Site A -- proxies for B C D\n"
6767 " +--Site D -- proxies for A B C\n"
6768 "Where A = http://foo.com/\n"
6769 " B = http://isolated.foo.com/\n"
6770 " C = http://b.isolated.foo.com/\n"
6771 " D = http://isolated.com/",
Fergal Daly79f44292020-12-01 02:30:486772 DepictFrameTree(*root));
Andrew Stone0a177fe22019-06-26 08:12:046773}
6774
Alex Moshchuk3b8eb3b2021-03-24 06:16:566775// Helper class for testing site isolation triggered by
6776// Cross-Origin-Opener-Policy headers. These tests disable strict site
6777// isolation by default, so that we can check whether a site becomes isolated
6778// due to COOP on both desktop and Android.
6779class COOPIsolationTest : public IsolatedOriginTestBase {
6780 public:
6781 // Note: the COOP header is only populated for HTTPS.
6782 COOPIsolationTest() : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
W. James MacLeancce43cd2024-06-24 14:51:046783 // Note: OriginKeyedProcessesByDefault should only apply when strict site
6784 // isolation is in effect, and these tests turn that off via
6785 // NoSiteIsolationContentBrowserClient.
6786 scoped_feature_list_.InitWithFeatures(
6787 /*enabled_features=*/{features::
6788 kSiteIsolationForCrossOriginOpenerPolicy},
6789 /*disabled_features=*/{features::kOriginKeyedProcessesByDefault});
Alex Moshchuk3b8eb3b2021-03-24 06:16:566790 }
6791
6792 ~COOPIsolationTest() override = default;
6793
6794 void SetUpCommandLine(base::CommandLine* command_line) override {
Arthur Sonzogni1dca7cf2021-08-06 07:49:496795 IsolatedOriginTestBase::SetUpCommandLine(command_line);
Alex Moshchuk3b8eb3b2021-03-24 06:16:566796 ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
Alex Moshchuk3b8eb3b2021-03-24 06:16:566797 }
6798
6799 void SetUpOnMainThread() override {
Arthur Sonzogni1dca7cf2021-08-06 07:49:496800 IsolatedOriginTestBase::SetUpOnMainThread();
Alex Moshchuk3b8eb3b2021-03-24 06:16:566801 host_resolver()->AddRule("*", "127.0.0.1");
6802 embedded_test_server()->StartAcceptingConnections();
6803
6804 https_server()->AddDefaultHandlers(GetTestDataFilePath());
6805 ASSERT_TRUE(https_server()->Start());
6806
Scott Violet99861992023-02-08 01:20:126807 browser_client_ = std::make_unique<NoSiteIsolationContentBrowserClient>();
Alex Moshchuk0c230b42021-10-15 01:37:306808
6809 // The custom ContentBrowserClient above typically ensures that this test
6810 // runs without strict site isolation, but it's still possible to
6811 // inadvertently override this when running with --site-per-process on the
6812 // command line. This might happen on try bots, so these tests take this
6813 // into account to prevent failures, but this is not an intended
6814 // configuration for these tests, since with strict site isolation COOP
6815 // doesn't need to dynamically isolate any sites.
6816 if (AreAllSitesIsolatedForTesting()) {
6817 LOG(WARNING) << "This test should be run without --site-per-process, "
6818 << "as it's designed to exercise code paths when strict "
6819 << "site isolation is turned off.";
6820 }
Alex Moshchuk3b8eb3b2021-03-24 06:16:566821 }
6822
6823 void TearDownOnMainThread() override {
Arthur Sonzogni1dca7cf2021-08-06 07:49:496824 IsolatedOriginTestBase::TearDownOnMainThread();
Scott Violet99861992023-02-08 01:20:126825 browser_client_.reset();
Alex Moshchuk3b8eb3b2021-03-24 06:16:566826 }
6827
6828 net::EmbeddedTestServer* https_server() { return &https_server_; }
6829
6830 // A custom ContentBrowserClient to turn off strict site isolation, since
6831 // COOP isolation only matters in environments like Android where it
6832 // is not used. Note that kSitePerProcess is a higher-layer feature, so we
6833 // can't just disable it here.
Scott Violet99861992023-02-08 01:20:126834 class NoSiteIsolationContentBrowserClient
6835 : public ContentBrowserTestContentBrowserClient {
Alex Moshchuk3b8eb3b2021-03-24 06:16:566836 public:
6837 bool ShouldEnableStrictSiteIsolation() override { return false; }
6838 };
6839
6840 private:
6841 base::test::ScopedFeatureList scoped_feature_list_;
6842
6843 net::EmbeddedTestServer https_server_;
6844
Scott Violet99861992023-02-08 01:20:126845 std::unique_ptr<NoSiteIsolationContentBrowserClient> browser_client_;
Alex Moshchuk3b8eb3b2021-03-24 06:16:566846};
6847
6848// Check that a main frame navigation to a COOP site (with no subsequent user
6849// gesture) triggers isolation for that site within the current
6850// BrowsingInstance.
6851IN_PROC_BROWSER_TEST_F(COOPIsolationTest, SameOrigin) {
Jagadesh P1c8699c22023-11-13 14:09:376852 if (AreAllSitesIsolatedForTesting()) {
Alex Moshchuk0c230b42021-10-15 01:37:306853 return;
Jagadesh P1c8699c22023-11-13 14:09:376854 }
Alex Moshchuk0c230b42021-10-15 01:37:306855
Alex Moshchuk3b8eb3b2021-03-24 06:16:566856 GURL no_coop_url = https_server()->GetURL("a.com", "/title1.html");
6857 EXPECT_TRUE(NavigateToURL(shell(), no_coop_url));
Dave Tapuska327c06c92022-06-13 20:31:516858 EXPECT_EQ(
6859 web_contents()->GetPrimaryMainFrame()->cross_origin_opener_policy().value,
6860 network::mojom::CrossOriginOpenerPolicyValue::kUnsafeNone);
Alex Moshchuk3b8eb3b2021-03-24 06:16:566861 scoped_refptr<SiteInstance> first_instance =
Dave Tapuska327c06c92022-06-13 20:31:516862 web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
Alex Moshchuk3b8eb3b2021-03-24 06:16:566863 EXPECT_FALSE(first_instance->RequiresDedicatedProcess());
6864
6865 // Navigate to a b.com URL with COOP, swapping BrowsingInstances.
6866 GURL coop_url = https_server()->GetURL(
6867 "b.com", "/set-header?Cross-Origin-Opener-Policy: same-origin");
6868 EXPECT_TRUE(NavigateToURL(shell(), coop_url));
Dave Tapuska327c06c92022-06-13 20:31:516869 EXPECT_EQ(
6870 web_contents()->GetPrimaryMainFrame()->cross_origin_opener_policy().value,
6871 network::mojom::CrossOriginOpenerPolicyValue::kSameOrigin);
Alex Moshchuk3b8eb3b2021-03-24 06:16:566872 SiteInstanceImpl* coop_instance =
Dave Tapuska327c06c92022-06-13 20:31:516873 web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
Alex Moshchuk3b8eb3b2021-03-24 06:16:566874 // The b.com COOP page should trigger the isolation heuristic and require a
6875 // dedicated process locked to b.com.
6876 EXPECT_TRUE(coop_instance->RequiresDedicatedProcess());
6877
Sharon Yang57481e52021-12-01 00:05:226878 auto lock = coop_instance->GetProcess()->GetProcessLock();
Alex Moshchuk3b8eb3b2021-03-24 06:16:566879 EXPECT_TRUE(lock.is_locked_to_site());
6880 EXPECT_EQ(ProcessLockFromUrl("https://b.com"), lock);
6881
6882 // Check that a cross-site subframe in a non-isolated site becomes an OOPIF
6883 // in a new, non-isolated SiteInstance.
Avi Drissmanc91bd8e2021-04-19 23:58:446884 ASSERT_TRUE(ExecJs(shell(),
6885 "var iframe = document.createElement('iframe');"
6886 "iframe.id = 'child';"
6887 "document.body.appendChild(iframe);",
6888 EXECUTE_SCRIPT_NO_USER_GESTURE));
Carlos Caballero15caeeb2021-10-27 09:57:556889 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Alex Moshchuk3b8eb3b2021-03-24 06:16:566890 FrameTreeNode* child = root->child_at(0);
6891 GURL c_url(https_server()->GetURL("c.com", "/title1.html"));
6892 EXPECT_TRUE(NavigateIframeToURL(web_contents(), "child", c_url));
6893 SiteInstanceImpl* child_instance =
6894 child->current_frame_host()->GetSiteInstance();
6895 EXPECT_NE(coop_instance, child_instance);
6896 EXPECT_NE(coop_instance->GetProcess(), child_instance->GetProcess());
6897 EXPECT_FALSE(child_instance->RequiresDedicatedProcess());
6898
6899 // Navigating the subframe back to b.com should bring it back to the parent
6900 // SiteInstance.
6901 GURL b_url(https_server()->GetURL("b.com", "/title1.html"));
6902 EXPECT_TRUE(NavigateIframeToURL(web_contents(), "child", b_url));
6903 child_instance = child->current_frame_host()->GetSiteInstance();
6904 EXPECT_EQ(coop_instance, child_instance);
6905
6906 // Create a new window, forcing a new BrowsingInstance, and check that b.com
6907 // is *not* isolated in it. Since b.com in `coop_instance`'s
6908 // BrowsingInstance hasn't received a user gesture, the COOP isolation does
6909 // not apply to other BrowsingInstances.
6910 Shell* new_shell = CreateBrowser();
6911 GURL no_coop_b_url = https_server()->GetURL("b.com", "/title2.html");
6912 EXPECT_TRUE(NavigateToURL(new_shell, no_coop_b_url));
6913 SiteInstanceImpl* new_instance = static_cast<SiteInstanceImpl*>(
Dave Tapuska327c06c92022-06-13 20:31:516914 new_shell->web_contents()->GetPrimaryMainFrame()->GetSiteInstance());
Alex Moshchuk3b8eb3b2021-03-24 06:16:566915 EXPECT_FALSE(new_instance->RequiresDedicatedProcess());
6916}
6917
6918// Verify that the same-origin-allow-popups COOP header value triggers
6919// isolation, and that this behaves sanely with window.open().
6920IN_PROC_BROWSER_TEST_F(COOPIsolationTest, SameOriginAllowPopups) {
Jagadesh P1c8699c22023-11-13 14:09:376921 if (AreAllSitesIsolatedForTesting()) {
Alex Moshchuk0c230b42021-10-15 01:37:306922 return;
Jagadesh P1c8699c22023-11-13 14:09:376923 }
Alex Moshchuk0c230b42021-10-15 01:37:306924
Alex Moshchuk3b8eb3b2021-03-24 06:16:566925 // Navigate to a coop.com URL with COOP.
6926 GURL coop_url = https_server()->GetURL(
6927 "coop.com",
6928 "/set-header?Cross-Origin-Opener-Policy: same-origin-allow-popups");
6929 EXPECT_TRUE(NavigateToURL(shell(), coop_url));
6930 EXPECT_EQ(
Dave Tapuska327c06c92022-06-13 20:31:516931 web_contents()->GetPrimaryMainFrame()->cross_origin_opener_policy().value,
Alex Moshchuk3b8eb3b2021-03-24 06:16:566932 network::mojom::CrossOriginOpenerPolicyValue::kSameOriginAllowPopups);
6933 SiteInstanceImpl* coop_instance =
Dave Tapuska327c06c92022-06-13 20:31:516934 web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
Alex Moshchuk3b8eb3b2021-03-24 06:16:566935 // The coop.com COOP page should trigger the isolation heuristic and require
6936 // a dedicated process locked to coop.com.
6937 EXPECT_TRUE(coop_instance->RequiresDedicatedProcess());
6938
Sharon Yang57481e52021-12-01 00:05:226939 auto lock = coop_instance->GetProcess()->GetProcessLock();
Alex Moshchuk3b8eb3b2021-03-24 06:16:566940 EXPECT_TRUE(lock.is_locked_to_site());
6941 EXPECT_EQ(ProcessLockFromUrl("https://coop.com"), lock);
6942
6943 // Open a non-COOP same-site URL in a popup, which should stay in the same
6944 // BrowsingInstance because of same-origin-allow-popups. Verify that the
6945 // popup ends up in the same SiteInstance as the opener (which requires a
6946 // dedicated process).
6947 GURL popup_url(https_server()->GetURL("coop.com", "/title1.html"));
6948 Shell* popup = OpenPopup(shell(), popup_url, "");
Dave Tapuska327c06c92022-06-13 20:31:516949 RenderFrameHostImpl* popup_rfh = static_cast<RenderFrameHostImpl*>(
6950 popup->web_contents()->GetPrimaryMainFrame());
Alex Moshchuk3b8eb3b2021-03-24 06:16:566951 EXPECT_EQ(popup_rfh->cross_origin_opener_policy().value,
6952 network::mojom::CrossOriginOpenerPolicyValue::kUnsafeNone);
6953 EXPECT_EQ(popup_rfh->GetSiteInstance(), coop_instance);
6954
6955 // Navigate the popup to another non-isolated site, staying in the same
6956 // BrowsingInstance, and verify that it swaps to a new non-isolated
6957 // SiteInstance. The non-isolated site has a child which is same-origin with
6958 // the COOP page; verify that it's placed in the same SiteInstance as the
6959 // COOP page, as they are allowed to synchronously script each other.
6960 GURL a_url(https_server()->GetURL(
6961 "a.com", "/cross_site_iframe_factory.html?a.com(coop.com)"));
6962 EXPECT_TRUE(NavigateToURLFromRenderer(popup, a_url));
6963 SiteInstanceImpl* new_instance = static_cast<SiteInstanceImpl*>(
Dave Tapuska327c06c92022-06-13 20:31:516964 popup->web_contents()->GetPrimaryMainFrame()->GetSiteInstance());
Alex Moshchuk3b8eb3b2021-03-24 06:16:566965 EXPECT_FALSE(new_instance->RequiresDedicatedProcess());
6966 EXPECT_NE(new_instance, coop_instance);
6967 FrameTreeNode* popup_child =
6968 static_cast<WebContentsImpl*>(popup->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:556969 ->GetPrimaryFrameTree()
6970 .root()
Alex Moshchuk3b8eb3b2021-03-24 06:16:566971 ->child_at(0);
6972 EXPECT_EQ(popup_child->current_frame_host()->GetSiteInstance(),
6973 coop_instance);
6974
6975 // Navigate the popup to coop.com again, staying in the same
6976 // BrowsingInstance, and verify that it goes back to the opener's
6977 // SiteInstance.
6978 EXPECT_TRUE(NavigateToURLFromRenderer(popup, popup_url));
Dave Tapuska327c06c92022-06-13 20:31:516979 EXPECT_EQ(popup->web_contents()->GetPrimaryMainFrame()->GetSiteInstance(),
Alex Moshchuk3b8eb3b2021-03-24 06:16:566980 coop_instance);
6981}
6982
Yoav Weiss354fee02024-10-07 10:17:526983class COOPIsolationNoopenerTest : public COOPIsolationTest {
6984 public:
6985 COOPIsolationNoopenerTest() {
6986 feature_list_.InitAndEnableFeature(
6987 network::features::kCoopNoopenerAllowPopups);
6988 }
6989
6990 private:
6991 base::test::ScopedFeatureList feature_list_;
6992};
6993
Yoav Weiss8ff864b2024-07-03 12:35:136994// Verify that the `noopener-allow-popups COOP header value triggers isolation,
6995// and that this behaves sanely with window.open().
Yoav Weiss354fee02024-10-07 10:17:526996IN_PROC_BROWSER_TEST_F(COOPIsolationNoopenerTest, NoopenerAllowPopups) {
Yoav Weiss8ff864b2024-07-03 12:35:136997 if (AreAllSitesIsolatedForTesting()) {
6998 return;
6999 }
7000
7001 // Navigate to a coop.com URL with no COOP.
7002 GURL coop_url = https_server()->GetURL(
7003 "coop.com", "/set-header?Cross-Origin-Opener-Policy: unsafe-none");
7004 EXPECT_TRUE(NavigateToURL(shell(), coop_url));
7005 EXPECT_EQ(
7006 web_contents()->GetPrimaryMainFrame()->cross_origin_opener_policy().value,
7007 network::mojom::CrossOriginOpenerPolicyValue::kUnsafeNone);
7008 SiteInstanceImpl* coop_instance =
7009 web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
7010 // The coop.com unsafe-none COOP page should not trigger the isolation
7011 // heuristic and not require a dedicated process locked to coop.com.
7012 EXPECT_FALSE(coop_instance->RequiresDedicatedProcess());
7013
7014 auto lock = coop_instance->GetProcess()->GetProcessLock();
7015 EXPECT_FALSE(lock.is_locked_to_site());
7016
7017 // Open a noopener-allow-popups COOP same-site URL in a popup, which should
7018 // swap a BrowsingInstance because of noopener-allow-popups. Verify that the
7019 // popup ends up in a different SiteInstance from the opener.
7020 GURL popup_url(https_server()->GetURL(
7021 "coop.com",
7022 "/set-header?Cross-Origin-Opener-Policy: noopener-allow-popups"));
7023 Shell* popup = OpenPopup(shell(), popup_url, "");
7024 RenderFrameHostImpl* popup_rfh = static_cast<RenderFrameHostImpl*>(
7025 popup->web_contents()->GetPrimaryMainFrame());
7026 EXPECT_EQ(popup_rfh->cross_origin_opener_policy().value,
Yoav Weiss354fee02024-10-07 10:17:527027 network::mojom::CrossOriginOpenerPolicyValue::kNoopenerAllowPopups);
7028 EXPECT_NE(popup_rfh->GetSiteInstance(), coop_instance);
7029 EXPECT_NE(popup_rfh->GetSiteInstance()->GetProcess(),
Yoav Weiss8ff864b2024-07-03 12:35:137030 coop_instance->GetProcess());
7031
7032 // Navigate the popup to another non-isolated site, staying in the same
7033 // BrowsingInstance, and verify that it swaps to a new non-isolated
7034 // SiteInstance. The non-isolated site has a child which is same-origin with
7035 // the COOP page; verify that it's placed in the same SiteInstance as the
7036 // COOP page, as they are allowed to synchronously script each other.
7037 GURL a_url(https_server()->GetURL(
7038 "a.com", "/cross_site_iframe_factory.html?a.com(coop.com)"));
7039 EXPECT_TRUE(NavigateToURLFromRenderer(popup, a_url));
7040 SiteInstanceImpl* new_instance = static_cast<SiteInstanceImpl*>(
7041 popup->web_contents()->GetPrimaryMainFrame()->GetSiteInstance());
7042 EXPECT_FALSE(new_instance->RequiresDedicatedProcess());
Yoav Weiss354fee02024-10-07 10:17:527043 EXPECT_NE(new_instance, coop_instance);
Yoav Weiss8ff864b2024-07-03 12:35:137044 FrameTreeNode* popup_child =
7045 static_cast<WebContentsImpl*>(popup->web_contents())
7046 ->GetPrimaryFrameTree()
7047 .root()
7048 ->child_at(0);
Yoav Weiss354fee02024-10-07 10:17:527049 EXPECT_NE(popup_child->current_frame_host()->GetSiteInstance(),
Yoav Weiss8ff864b2024-07-03 12:35:137050 coop_instance);
7051
7052 // Navigate the popup to coop.com again, staying in the same
7053 // BrowsingInstance, and verify that it goes back to the opener's
7054 // SiteInstance.
7055 EXPECT_TRUE(NavigateToURLFromRenderer(popup, popup_url));
Yoav Weiss354fee02024-10-07 10:17:527056 EXPECT_NE(popup->web_contents()->GetPrimaryMainFrame()->GetSiteInstance(),
Yoav Weiss8ff864b2024-07-03 12:35:137057 coop_instance);
7058}
7059
Alex Moshchuk3b8eb3b2021-03-24 06:16:567060// Verify that COOP isolation applies at a site (and not origin) granularity.
7061//
7062// Isolating sites rather than origins may seem counterintuitive, considering
7063// the COOP header value that triggers isolation is "same-origin". However,
7064// process isolation granularity that we can infer from COOP is quite different
7065// from what that actual COOP value controls. The COOP "same-origin" value
7066// specifies when to sever opener relationships and create a new
7067// BrowsingInstance; a COOP "same-origin" main frame document may only stay in
7068// the same BrowsingInstance as other same-origin COOP documents. However,
7069// this does not apply to iframes, and it's possible to have a
7070// foo.bar.coop.com(baz.coop.com) hierarchy where the main frame has COOP
7071// "same-origin" but both frames set document.domain to coop.com and
7072// synchronously script each other (*). Hence, in this case, we must isolate
7073// the coop.com site and place the two frames in the same process. This test
7074// covers that precise scenario.
7075//
7076// (*) In the future, COOP may disallow document.domain, in which case we may
7077// need to revisit this. See https://github.com/whatwg/html/issues/6177.
7078IN_PROC_BROWSER_TEST_F(COOPIsolationTest, SiteGranularity) {
Jagadesh P1c8699c22023-11-13 14:09:377079 if (AreAllSitesIsolatedForTesting()) {
Alex Moshchuk0c230b42021-10-15 01:37:307080 return;
Jagadesh P1c8699c22023-11-13 14:09:377081 }
Alex Moshchuk0c230b42021-10-15 01:37:307082
Alex Moshchuk3b8eb3b2021-03-24 06:16:567083 // Navigate to a URL with COOP, where the origin doesn't match the site.
7084 GURL coop_url = https_server()->GetURL(
7085 "foo.bar.coop.com",
7086 "/set-header?Cross-Origin-Opener-Policy: same-origin");
7087 EXPECT_TRUE(NavigateToURL(shell(), coop_url));
Dave Tapuska327c06c92022-06-13 20:31:517088 EXPECT_EQ(
7089 web_contents()->GetPrimaryMainFrame()->cross_origin_opener_policy().value,
7090 network::mojom::CrossOriginOpenerPolicyValue::kSameOrigin);
Alex Moshchuk3b8eb3b2021-03-24 06:16:567091 SiteInstanceImpl* coop_instance =
Dave Tapuska327c06c92022-06-13 20:31:517092 web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
Alex Moshchuk3b8eb3b2021-03-24 06:16:567093 EXPECT_TRUE(coop_instance->RequiresDedicatedProcess());
7094
7095 // Ensure that the process lock is for the site, not origin.
Sharon Yang57481e52021-12-01 00:05:227096 auto lock = coop_instance->GetProcess()->GetProcessLock();
Alex Moshchuk3b8eb3b2021-03-24 06:16:567097 EXPECT_TRUE(lock.is_locked_to_site());
7098 EXPECT_EQ(ProcessLockFromUrl("https://coop.com"), lock);
7099
7100 // Check that a same-site cross-origin subframe stays in the same
7101 // SiteInstance and process.
Avi Drissmanc91bd8e2021-04-19 23:58:447102 ASSERT_TRUE(ExecJs(shell(),
7103 "var iframe = document.createElement('iframe');"
7104 "iframe.id = 'child';"
7105 "document.body.appendChild(iframe);"));
Carlos Caballero15caeeb2021-10-27 09:57:557106 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Alex Moshchuk3b8eb3b2021-03-24 06:16:567107 FrameTreeNode* child = root->child_at(0);
7108 GURL c_url(https_server()->GetURL("baz.coop.com", "/title1.html"));
7109 EXPECT_TRUE(NavigateIframeToURL(web_contents(), "child", c_url));
7110 SiteInstanceImpl* child_instance =
7111 child->current_frame_host()->GetSiteInstance();
7112 EXPECT_EQ(coop_instance, child_instance);
7113
7114 // Check that ChildProcessSecurityPolicy considers coop.com (and not its
7115 // subdomain) to be the matching isolated origin for `coop_url`.
7116 url::Origin matching_isolated_origin;
Sharon Yang57481e52021-12-01 00:05:227117 ChildProcessSecurityPolicyImpl::GetInstance()
7118 ->GetMatchingProcessIsolatedOrigin(coop_instance->GetIsolationContext(),
7119 url::Origin::Create(GURL(coop_url)),
7120 false /* origin_requests_isolation */,
7121 &matching_isolated_origin);
Alex Moshchuk3b8eb3b2021-03-24 06:16:567122 EXPECT_EQ(matching_isolated_origin,
7123 url::Origin::Create(GURL("https://coop.com")));
7124}
7125
7126// Verify that COOP isolation applies when both COOP and COEP headers are set
7127// (i.e., for a cross-origin-isolated page). This results in a different COOP
7128// header value (kSameOriginPlusCoep) which should still trigger isolation.
7129IN_PROC_BROWSER_TEST_F(COOPIsolationTest, COOPAndCOEP) {
7130 // Navigate to a URL with COOP + COEP.
7131 GURL coop_url = https_server()->GetURL(
7132 "coop.com",
7133 "/set-header?Cross-Origin-Opener-Policy: same-origin&"
7134 "Cross-Origin-Embedder-Policy: require-corp");
7135 EXPECT_TRUE(NavigateToURL(shell(), coop_url));
Dave Tapuska327c06c92022-06-13 20:31:517136 EXPECT_EQ(
7137 web_contents()->GetPrimaryMainFrame()->cross_origin_opener_policy().value,
7138 network::mojom::CrossOriginOpenerPolicyValue::kSameOriginPlusCoep);
Alex Moshchuk3b8eb3b2021-03-24 06:16:567139
7140 // Make sure that site isolation for coop.com was triggered and that the
7141 // navigation ended up in a site-locked process.
7142 SiteInstanceImpl* coop_instance =
Dave Tapuska327c06c92022-06-13 20:31:517143 web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
Alex Moshchuk3b8eb3b2021-03-24 06:16:567144 EXPECT_TRUE(coop_instance->RequiresDedicatedProcess());
Sharon Yang57481e52021-12-01 00:05:227145 auto lock = coop_instance->GetProcess()->GetProcessLock();
Sharon Yanga6942552021-11-16 21:15:097146 EXPECT_TRUE(lock.GetWebExposedIsolationInfo().is_isolated());
Alex Moshchuk3b8eb3b2021-03-24 06:16:567147 EXPECT_TRUE(lock.is_locked_to_site());
7148 EXPECT_TRUE(
7149 lock.MatchesOrigin(url::Origin::Create(GURL("https://coop.com"))));
7150}
7151
7152// Check that when a site triggers both COOP isolation and OriginAgentCluster,
7153// both mechanisms take effect. This test uses a URL with default ports so
7154// that we can exercise the site URL being the same with both COOP and OAC.
7155IN_PROC_BROWSER_TEST_F(COOPIsolationTest, COOPAndOriginAgentClusterNoPorts) {
7156 // Since the embedded test server only works for URLs with non-default ports,
7157 // use a URLLoaderInterceptor to mimic port-free operation. This allows
7158 // checking the site URL being identical for both COOP and OAC isolation,
7159 // since otherwise OAC would include ports in the site URL. The interceptor
7160 // below returns COOP and OAC headers for any page on foo.com, and returns a
7161 // simple test page without any headers for a.foo.com and b.foo.com.
7162 URLLoaderInterceptor interceptor(base::BindLambdaForTesting(
7163 [&](URLLoaderInterceptor::RequestParams* params) {
7164 if (params->url_request.url.host() == "foo.com") {
7165 const std::string headers =
7166 "HTTP/1.1 200 OK\n"
7167 "Content-Type: text/html\n"
7168 "Origin-Agent-Cluster: ?1\n"
7169 "Cross-Origin-Opener-Policy: same-origin\n";
7170 URLLoaderInterceptor::WriteResponse(
7171 "content/test/data" + params->url_request.url.path(),
Arthur Sonzognic686e8f2024-01-11 08:36:377172 params->client.get(), &headers, std::optional<net::SSLInfo>());
Alex Moshchuk3b8eb3b2021-03-24 06:16:567173 return true;
7174 } else if (params->url_request.url.host() == "a.foo.com" ||
7175 params->url_request.url.host() == "b.foo.com") {
7176 URLLoaderInterceptor::WriteResponse("content/test/data/title1.html",
7177 params->client.get());
7178 return true;
7179 }
7180 // Not handled by us.
7181 return false;
7182 }));
7183
7184 // Navigate to a URL with with COOP and OriginAgentCluster headers, embedding
7185 // two iframes at a.foo.com and b.foo.com.
7186 GURL coop_oac_url(
7187 "https://foo.com/cross_site_iframe_factory.html?"
7188 "foo.com(a.foo.com,b.foo.com)");
7189 EXPECT_TRUE(NavigateToURL(shell(), coop_oac_url));
Dave Tapuska327c06c92022-06-13 20:31:517190 EXPECT_EQ(
7191 web_contents()->GetPrimaryMainFrame()->cross_origin_opener_policy().value,
7192 network::mojom::CrossOriginOpenerPolicyValue::kSameOrigin);
Alex Moshchuk3b8eb3b2021-03-24 06:16:567193
Carlos Caballero15caeeb2021-10-27 09:57:557194 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Alex Moshchuk3b8eb3b2021-03-24 06:16:567195 FrameTreeNode* child1 = root->child_at(0);
7196 FrameTreeNode* child2 = root->child_at(1);
7197
7198 // The two subframes should end up in the same SiteInstance, different from
7199 // the main frame's SiteInstance. Both SiteInstances should be in a process
7200 // dedicated to foo.com, but the main frame's process should be for
7201 // origin-keyed foo.com (strictly foo.com excluding subdomains) due to
7202 // Origin-Agent-Cluster, whereas the subframe process should be for
7203 // site-keyed foo.com.
7204 SiteInstanceImpl* main_instance =
Dave Tapuska327c06c92022-06-13 20:31:517205 web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
Alex Moshchuk3b8eb3b2021-03-24 06:16:567206 SiteInstanceImpl* child_instance =
7207 child1->current_frame_host()->GetSiteInstance();
7208 EXPECT_EQ(child_instance, child2->current_frame_host()->GetSiteInstance());
7209 EXPECT_NE(child_instance, main_instance);
7210
7211 EXPECT_TRUE(main_instance->RequiresDedicatedProcess());
7212 EXPECT_TRUE(child_instance->RequiresDedicatedProcess());
7213
W. James MacLean7f76c2202021-11-15 16:27:497214 EXPECT_TRUE(main_instance->GetSiteInfo().requires_origin_keyed_process());
7215 EXPECT_FALSE(child_instance->GetSiteInfo().requires_origin_keyed_process());
Alex Moshchuk3b8eb3b2021-03-24 06:16:567216 EXPECT_EQ(main_instance->GetSiteInfo().site_url(),
7217 child_instance->GetSiteInfo().site_url());
7218 EXPECT_EQ(main_instance->GetSiteInfo().process_lock_url(),
7219 child_instance->GetSiteInfo().process_lock_url());
7220
Sharon Yang57481e52021-12-01 00:05:227221 auto main_lock = main_instance->GetProcess()->GetProcessLock();
7222 auto child_lock = child_instance->GetProcess()->GetProcessLock();
Alex Moshchuk3b8eb3b2021-03-24 06:16:567223 EXPECT_TRUE(main_lock.is_locked_to_site());
7224 EXPECT_TRUE(child_lock.is_locked_to_site());
W. James MacLean7f76c2202021-11-15 16:27:497225 EXPECT_TRUE(main_lock.is_origin_keyed_process());
7226 EXPECT_FALSE(child_lock.is_origin_keyed_process());
Alex Moshchuk3b8eb3b2021-03-24 06:16:567227 auto foo_origin = url::Origin::Create(GURL("https://foo.com"));
7228 EXPECT_TRUE(main_lock.MatchesOrigin(foo_origin));
7229 EXPECT_TRUE(child_lock.MatchesOrigin(foo_origin));
7230}
7231
7232// Check that when a site triggers both COOP isolation and OriginAgentCluster,
7233// both mechanisms take effect. Similar to the test above, but starts on a URL
7234// where the origin doesn't match the site.
7235IN_PROC_BROWSER_TEST_F(COOPIsolationTest,
7236 COOPAndOriginAgentClusterOnSubdomain) {
7237 // Navigate to a URL with with COOP and OriginAgentCluster headers.
7238 GURL coop_oac_url = https_server()->GetURL(
7239 "oac.coop.com",
7240 "/set-header?Cross-Origin-Opener-Policy: same-origin&"
7241 "Origin-Agent-Cluster: ?1");
7242 EXPECT_TRUE(NavigateToURL(shell(), coop_oac_url));
Dave Tapuska327c06c92022-06-13 20:31:517243 EXPECT_EQ(
7244 web_contents()->GetPrimaryMainFrame()->cross_origin_opener_policy().value,
7245 network::mojom::CrossOriginOpenerPolicyValue::kSameOrigin);
Alex Moshchuk3b8eb3b2021-03-24 06:16:567246
Carlos Caballero15caeeb2021-10-27 09:57:557247 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Alex Moshchuk3b8eb3b2021-03-24 06:16:567248
7249 // Add a subframe and navigate to foo.coop.com.
Avi Drissmanc91bd8e2021-04-19 23:58:447250 ASSERT_TRUE(ExecJs(shell(),
7251 "var iframe = document.createElement('iframe');"
7252 "iframe.id = 'child';"
7253 "document.body.appendChild(iframe);"));
Alex Moshchuk3b8eb3b2021-03-24 06:16:567254 FrameTreeNode* child = root->child_at(0);
7255 GURL child_url(https_server()->GetURL("foo.coop.com", "/title1.html"));
7256 EXPECT_TRUE(NavigateIframeToURL(web_contents(), "child", child_url));
7257
7258 // The subframe should end up in a different SiteInstance from the main
7259 // frame's SiteInstance. The main frame's SiteInstance should be in an
7260 // origin-keyed process locked to oac.foo.com, whereas the child's
7261 // SiteInstance should be in a site-keyed process locked to foo.com.
7262 SiteInstanceImpl* main_instance =
Dave Tapuska327c06c92022-06-13 20:31:517263 web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
Alex Moshchuk3b8eb3b2021-03-24 06:16:567264 SiteInstanceImpl* child_instance =
7265 child->current_frame_host()->GetSiteInstance();
7266 EXPECT_NE(child_instance, main_instance);
7267
7268 EXPECT_TRUE(main_instance->RequiresDedicatedProcess());
7269 EXPECT_TRUE(child_instance->RequiresDedicatedProcess());
7270
W. James MacLean7f76c2202021-11-15 16:27:497271 EXPECT_TRUE(main_instance->GetSiteInfo().requires_origin_keyed_process());
7272 EXPECT_FALSE(child_instance->GetSiteInfo().requires_origin_keyed_process());
Alex Moshchuk3b8eb3b2021-03-24 06:16:567273 EXPECT_NE(main_instance->GetSiteInfo().site_url(),
7274 child_instance->GetSiteInfo().site_url());
7275 EXPECT_NE(main_instance->GetSiteInfo().process_lock_url(),
7276 child_instance->GetSiteInfo().process_lock_url());
7277
Sharon Yang57481e52021-12-01 00:05:227278 auto main_lock = main_instance->GetProcess()->GetProcessLock();
7279 auto child_lock = child_instance->GetProcess()->GetProcessLock();
Alex Moshchuk3b8eb3b2021-03-24 06:16:567280 EXPECT_TRUE(main_lock.is_locked_to_site());
7281 EXPECT_TRUE(child_lock.is_locked_to_site());
W. James MacLean7f76c2202021-11-15 16:27:497282 EXPECT_TRUE(main_lock.is_origin_keyed_process());
7283 EXPECT_FALSE(child_lock.is_origin_keyed_process());
Alex Moshchuk3b8eb3b2021-03-24 06:16:567284 auto oac_coop_origin = url::Origin::Create(coop_oac_url);
7285 auto coop_origin = url::Origin::Create(GURL("https://coop.com"));
7286 EXPECT_TRUE(main_lock.MatchesOrigin(oac_coop_origin));
7287 EXPECT_TRUE(child_lock.MatchesOrigin(coop_origin));
7288}
7289
7290// Verify that if strict site isolation is in place, COOP isolation does not
7291// add redundant isolated origins to ChildProcessSecurityPolicy.
7292IN_PROC_BROWSER_TEST_F(COOPIsolationTest, SiteAlreadyRequiresDedicatedProcess) {
7293 // Enable --site-per-process and navigate to a COOP-enabled document.
7294 IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
7295 GURL coop_url = https_server()->GetURL(
7296 "coop.com", "/set-header?Cross-Origin-Opener-Policy: same-origin");
7297 EXPECT_TRUE(NavigateToURL(shell(), coop_url));
Alex Moshchuke99d3212021-05-19 22:37:457298 // Simulate user activation, which normally triggers COOP isolation for
7299 // future BrowsingInstances.
7300 EXPECT_TRUE(ExecJs(shell(), "// no-op"));
Dave Tapuska327c06c92022-06-13 20:31:517301 EXPECT_EQ(
7302 web_contents()->GetPrimaryMainFrame()->cross_origin_opener_policy().value,
7303 network::mojom::CrossOriginOpenerPolicyValue::kSameOrigin);
Alex Moshchuk3b8eb3b2021-03-24 06:16:567304 SiteInstanceImpl* coop_instance =
Dave Tapuska327c06c92022-06-13 20:31:517305 web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
Alex Moshchuk3b8eb3b2021-03-24 06:16:567306
7307 // The SiteInstance should require a dedicated process, but
7308 // ChildProcessSecurityPolicy shouldn't have added an isolated origin
7309 // for coop.com.
7310 EXPECT_TRUE(coop_instance->RequiresDedicatedProcess());
7311 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
7312 auto origins = policy->GetIsolatedOrigins(
7313 ChildProcessSecurityPolicy::IsolatedOriginSource::WEB_TRIGGERED);
7314 EXPECT_EQ(0U, origins.size());
7315 EXPECT_FALSE(policy->IsIsolatedOrigin(coop_instance->GetIsolationContext(),
7316 url::Origin::Create(coop_url),
7317 false /* origin_requests_isolation */));
7318}
7319
Alex Moshchuk03904192021-04-02 07:29:087320// Verify that seeing a user activation on a COOP document triggers isolation
7321// of that document's site in future BrowsingInstances, but doesn't affect any
7322// existing BrowsingInstances.
7323IN_PROC_BROWSER_TEST_F(COOPIsolationTest, UserActivation) {
Jagadesh P1c8699c22023-11-13 14:09:377324 if (AreAllSitesIsolatedForTesting()) {
Alex Moshchuk0c230b42021-10-15 01:37:307325 return;
Jagadesh P1c8699c22023-11-13 14:09:377326 }
Alex Moshchuk0c230b42021-10-15 01:37:307327
Alex Moshchuk03904192021-04-02 07:29:087328 GURL coop_url = https_server()->GetURL(
7329 "b.com", "/set-header?Cross-Origin-Opener-Policy: same-origin");
7330 EXPECT_TRUE(NavigateToURL(shell(), coop_url));
Dave Tapuska327c06c92022-06-13 20:31:517331 EXPECT_EQ(
7332 web_contents()->GetPrimaryMainFrame()->cross_origin_opener_policy().value,
7333 network::mojom::CrossOriginOpenerPolicyValue::kSameOrigin);
Carlos Caballero15caeeb2021-10-27 09:57:557334 FrameTreeNode* coop_root = web_contents()->GetPrimaryFrameTree().root();
Alex Moshchuk03904192021-04-02 07:29:087335 SiteInstance* coop_instance =
Dave Tapuska327c06c92022-06-13 20:31:517336 web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
Alex Moshchuk03904192021-04-02 07:29:087337 // The b.com COOP page should trigger the isolation heuristic and require a
7338 // dedicated process locked to b.com.
7339 EXPECT_TRUE(coop_instance->RequiresDedicatedProcess());
7340
7341 // At this point, the COOP page shouldn't have user activation.
7342 EXPECT_FALSE(coop_root->HasTransientUserActivation());
7343
7344 // Create a new window, forcing a new BrowsingInstance, and check that b.com
7345 // is *not* isolated in it. Since b.com in `coop_instance`'s
7346 // BrowsingInstance hasn't been interacted with, the COOP isolation does not
7347 // apply to other BrowsingInstances yet.
7348 Shell* shell2 = CreateBrowser();
7349 GURL no_coop_b_url = https_server()->GetURL("b.com", "/title2.html");
7350 EXPECT_TRUE(NavigateToURL(shell2, no_coop_b_url));
7351 FrameTreeNode* shell2_root =
7352 static_cast<WebContentsImpl*>(shell2->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:557353 ->GetPrimaryFrameTree()
7354 .root();
Alex Moshchuk03904192021-04-02 07:29:087355 scoped_refptr<SiteInstance> instance2 =
Dave Tapuska327c06c92022-06-13 20:31:517356 shell2->web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
Alex Moshchuk03904192021-04-02 07:29:087357 EXPECT_FALSE(instance2->RequiresDedicatedProcess());
7358
7359 // Simulate a user activation in the original COOP page by running a dummy
Chris Fredricksond3bb2682023-05-10 03:32:267360 // script (ExecJs sends user activation by default).
Avi Drissmanc91bd8e2021-04-19 23:58:447361 EXPECT_TRUE(ExecJs(coop_root, "// no-op"));
Alex Moshchuk03904192021-04-02 07:29:087362 EXPECT_TRUE(coop_root->HasTransientUserActivation());
7363
7364 // Create a third window in a new BrowsingInstance and navigate it to a
7365 // non-COOP b.com URL. The above user activation should've forced COOP
7366 // isolation for b.com to apply to future BrowsingInstances, so check that
7367 // this navigation ends up requiring a dedicated process.
7368 Shell* shell3 = CreateBrowser();
7369 EXPECT_TRUE(NavigateToURL(shell3, no_coop_b_url));
7370 SiteInstance* instance3 =
Dave Tapuska327c06c92022-06-13 20:31:517371 shell3->web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
Alex Moshchuk03904192021-04-02 07:29:087372 EXPECT_TRUE(instance3->RequiresDedicatedProcess());
7373 EXPECT_FALSE(instance2->IsRelatedSiteInstance(instance3));
7374 EXPECT_FALSE(coop_instance->IsRelatedSiteInstance(instance3));
7375
7376 // Ensure that the older BrowsingInstance in the second window wasn't
7377 // affected by the new isolation. Adding a b.com subframe or popup should
7378 // stay in the same SiteInstance. Navigating the popup out from and back to
7379 // b.com should also end up on the same SiteInstance.
Avi Drissmanc91bd8e2021-04-19 23:58:447380 ASSERT_TRUE(ExecJs(shell2,
7381 "var iframe = document.createElement('iframe');"
7382 "iframe.id = 'child';"
7383 "document.body.appendChild(iframe);",
7384 EXECUTE_SCRIPT_NO_USER_GESTURE));
Alex Moshchuk03904192021-04-02 07:29:087385 FrameTreeNode* child = shell2_root->child_at(0);
7386 GURL another_b_url(https_server()->GetURL("b.com", "/title3.html"));
7387 EXPECT_TRUE(
7388 NavigateIframeToURL(shell2->web_contents(), "child", another_b_url));
7389 SiteInstanceImpl* child_instance =
7390 child->current_frame_host()->GetSiteInstance();
7391 EXPECT_EQ(child_instance, instance2);
7392
7393 Shell* popup = OpenPopup(shell2, another_b_url, "");
7394 FrameTreeNode* popup_root =
7395 static_cast<WebContentsImpl*>(popup->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:557396 ->GetPrimaryFrameTree()
7397 .root();
Alex Moshchuk03904192021-04-02 07:29:087398 EXPECT_EQ(popup_root->current_frame_host()->GetSiteInstance(), instance2);
7399
7400 EXPECT_TRUE(NavigateToURLFromRenderer(
7401 popup, https_server()->GetURL("c.com", "/title1.html")));
7402 EXPECT_TRUE(NavigateToURLFromRenderer(popup, another_b_url));
7403 EXPECT_EQ(popup_root->current_frame_host()->GetSiteInstance(), instance2);
7404
7405 // Close the popup.
7406 popup->Close();
7407
7408 // Without any related windows, navigating to b.com in the second window's
7409 // main frame should trigger a proactive BrowsingInstance swap (see
7410 // ShouldSwapBrowsingInstancesForDynamicIsolation()), since we notice that
7411 // b.com would be isolated in a fresh BrowsingInstance, and nothing prevents
7412 // the BrowsingInstance swap. Hence, in that case, the navigation should be
7413 // in a new BrowsingInstance and in an isolated process.
7414 EXPECT_TRUE(NavigateToURLFromRenderer(
7415 shell2, https_server()->GetURL("b.com", "/title3.html")));
7416 scoped_refptr<SiteInstance> instance2_new =
Dave Tapuska327c06c92022-06-13 20:31:517417 shell2->web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
Alex Moshchuk03904192021-04-02 07:29:087418 EXPECT_TRUE(instance2_new->RequiresDedicatedProcess());
7419 EXPECT_NE(instance2_new, instance2);
7420 EXPECT_FALSE(instance2_new->IsRelatedSiteInstance(instance2.get()));
7421}
7422
7423// Similar to the test above, but verify that a user activation on a same-site
7424// subframe also triggers isolation of a COOP site in the main frame for future
7425// BrowsingInstances.
7426IN_PROC_BROWSER_TEST_F(COOPIsolationTest, UserActivationInSubframe) {
Jagadesh P1c8699c22023-11-13 14:09:377427 if (AreAllSitesIsolatedForTesting()) {
Alex Moshchuk0c230b42021-10-15 01:37:307428 return;
Jagadesh P1c8699c22023-11-13 14:09:377429 }
Alex Moshchuk0c230b42021-10-15 01:37:307430
Alex Moshchuk03904192021-04-02 07:29:087431 GURL coop_url = https_server()->GetURL(
7432 "b.com", "/set-header?Cross-Origin-Opener-Policy: same-origin");
7433 EXPECT_TRUE(NavigateToURL(shell(), coop_url));
Dave Tapuska327c06c92022-06-13 20:31:517434 EXPECT_EQ(
7435 web_contents()->GetPrimaryMainFrame()->cross_origin_opener_policy().value,
7436 network::mojom::CrossOriginOpenerPolicyValue::kSameOrigin);
Alex Moshchuk03904192021-04-02 07:29:087437 SiteInstance* coop_instance =
Dave Tapuska327c06c92022-06-13 20:31:517438 web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
Alex Moshchuk03904192021-04-02 07:29:087439 EXPECT_TRUE(coop_instance->RequiresDedicatedProcess());
7440
7441 // Add a cross-site subframe.
Avi Drissmanc91bd8e2021-04-19 23:58:447442 ASSERT_TRUE(ExecJs(shell(),
7443 "var iframe = document.createElement('iframe');"
7444 "iframe.id = 'child';"
7445 "document.body.appendChild(iframe);",
7446 EXECUTE_SCRIPT_NO_USER_GESTURE));
Carlos Caballero15caeeb2021-10-27 09:57:557447 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Alex Moshchuk03904192021-04-02 07:29:087448 FrameTreeNode* child = root->child_at(0);
7449 GURL c_url(https_server()->GetURL("c.com", "/title1.html"));
7450 EXPECT_TRUE(NavigateIframeToURL(web_contents(), "child", c_url));
7451
7452 EXPECT_FALSE(root->HasTransientUserActivation());
7453 EXPECT_FALSE(child->HasTransientUserActivation());
7454
7455 // Simulate a user activation in the subframe by running a dummy script.
Avi Drissmanc91bd8e2021-04-19 23:58:447456 EXPECT_TRUE(ExecJs(child, "// no-op"));
Alex Moshchuk03904192021-04-02 07:29:087457 EXPECT_TRUE(child->HasTransientUserActivation());
7458
7459 // Since the iframe is cross-origin, it shouldn't trigger isolation of b.com
7460 // for future BrowsingInstances.
7461 GURL no_coop_b_url = https_server()->GetURL("b.com", "/title2.html");
7462 {
7463 Shell* new_shell = CreateBrowser();
7464 EXPECT_TRUE(NavigateToURL(new_shell, no_coop_b_url));
7465 scoped_refptr<SiteInstance> instance =
Dave Tapuska327c06c92022-06-13 20:31:517466 new_shell->web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
Alex Moshchuk03904192021-04-02 07:29:087467 EXPECT_FALSE(instance->RequiresDedicatedProcess());
7468 }
7469
7470 // Now, make the iframe same-origin and simulate a user gesture.
7471 GURL b_url(https_server()->GetURL("b.com", "/title1.html"));
7472 EXPECT_TRUE(NavigateIframeToURL(web_contents(), "child", b_url));
7473
Avi Drissmanc91bd8e2021-04-19 23:58:447474 EXPECT_TRUE(ExecJs(child, "// no-op"));
Alex Moshchuk03904192021-04-02 07:29:087475
7476 // Ensure that b.com is now isolated in a new tab and BrowsingInstance.
7477 {
7478 Shell* new_shell = CreateBrowser();
7479 EXPECT_TRUE(NavigateToURL(new_shell, no_coop_b_url));
7480 scoped_refptr<SiteInstance> instance =
Dave Tapuska327c06c92022-06-13 20:31:517481 new_shell->web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
Alex Moshchuk03904192021-04-02 07:29:087482 EXPECT_TRUE(instance->RequiresDedicatedProcess());
7483 }
7484}
7485
7486// Similar to the test above, but verify that a user activation on a
7487// same-origin about:blank subframe triggers isolation of a COOP site in the
7488// main frame for future BrowsingInstances.
7489IN_PROC_BROWSER_TEST_F(COOPIsolationTest, UserActivationInAboutBlankSubframe) {
7490 GURL coop_url = https_server()->GetURL(
7491 "b.com", "/set-header?Cross-Origin-Opener-Policy: same-origin");
7492 EXPECT_TRUE(NavigateToURL(shell(), coop_url));
Dave Tapuska327c06c92022-06-13 20:31:517493 EXPECT_EQ(
7494 web_contents()->GetPrimaryMainFrame()->cross_origin_opener_policy().value,
7495 network::mojom::CrossOriginOpenerPolicyValue::kSameOrigin);
Alex Moshchuk03904192021-04-02 07:29:087496 SiteInstance* coop_instance =
Dave Tapuska327c06c92022-06-13 20:31:517497 web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
Alex Moshchuk03904192021-04-02 07:29:087498 EXPECT_TRUE(coop_instance->RequiresDedicatedProcess());
7499
7500 // Add a cross-site blank subframe.
Avi Drissmanc91bd8e2021-04-19 23:58:447501 ASSERT_TRUE(ExecJs(shell(),
7502 "var iframe = document.createElement('iframe');"
7503 "iframe.id = 'child';"
7504 "document.body.appendChild(iframe);",
7505 EXECUTE_SCRIPT_NO_USER_GESTURE));
Carlos Caballero15caeeb2021-10-27 09:57:557506 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Alex Moshchuk03904192021-04-02 07:29:087507 FrameTreeNode* child = root->child_at(0);
7508
7509 EXPECT_FALSE(root->HasTransientUserActivation());
7510 EXPECT_FALSE(child->HasTransientUserActivation());
7511
7512 // Simulate a user activation in the subframe by running a dummy script.
Avi Drissmanc91bd8e2021-04-19 23:58:447513 EXPECT_TRUE(ExecJs(child, "// no-op"));
Alex Moshchuk03904192021-04-02 07:29:087514 EXPECT_TRUE(child->HasTransientUserActivation());
7515
7516 // Ensure that b.com is isolated in a new tab and BrowsingInstance.
7517 {
7518 Shell* new_shell = CreateBrowser();
7519 GURL no_coop_b_url = https_server()->GetURL("b.com", "/title2.html");
7520 EXPECT_TRUE(NavigateToURL(new_shell, no_coop_b_url));
7521 scoped_refptr<SiteInstance> instance =
Dave Tapuska327c06c92022-06-13 20:31:517522 new_shell->web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
Alex Moshchuk03904192021-04-02 07:29:087523 EXPECT_TRUE(instance->RequiresDedicatedProcess());
7524 }
7525}
Andrew Williams61e816d2021-11-01 04:41:047526
Alex Moshchuked57bfe2021-12-06 22:17:127527// Ensure that navigating to http://localhost which has COOP+COEP headers, and
7528// hence will attempt to trigger COOP isolation, will not crash. See
7529// https://crbug.com/1276155.
7530IN_PROC_BROWSER_TEST_F(COOPIsolationTest, Localhost) {
7531 // Navigate to a URL with COOP + COEP on http://localhost.
7532 GURL coop_url = https_server()->GetURL(
7533 "localhost",
7534 "/set-header?Cross-Origin-Opener-Policy: same-origin&"
7535 "Cross-Origin-Embedder-Policy: require-corp");
7536 EXPECT_TRUE(NavigateToURL(shell(), coop_url));
Dave Tapuska327c06c92022-06-13 20:31:517537 EXPECT_EQ(
7538 web_contents()->GetPrimaryMainFrame()->cross_origin_opener_policy().value,
7539 network::mojom::CrossOriginOpenerPolicyValue::kSameOriginPlusCoep);
Alex Moshchuked57bfe2021-12-06 22:17:127540
7541 // http://localhost isn't currently considered a valid isolated origin (since
7542 // it won't work for subdomain matching), so the navigation should not
7543 // trigger site isolation. Note, however, that the process lock should still
7544 // reflect COOP+COEP isolation.
7545 SiteInstanceImpl* coop_instance =
Dave Tapuska327c06c92022-06-13 20:31:517546 web_contents()->GetPrimaryMainFrame()->GetSiteInstance();
Alex Moshchuked57bfe2021-12-06 22:17:127547 EXPECT_FALSE(coop_instance->RequiresDedicatedProcess());
7548 auto lock = coop_instance->GetProcess()->GetProcessLock();
7549 EXPECT_TRUE(lock.GetWebExposedIsolationInfo().is_isolated());
7550 EXPECT_FALSE(lock.is_locked_to_site());
7551}
7552
Will Harris2f439d0d2021-06-16 21:52:477553// Helper class for testing site isolation triggered by different JIT policies
Sharon Yang01fdd492025-05-22 19:45:337554// being applied. Parameterized to run with JIT enabled and disabled by default.
Will Harris2f439d0d2021-06-16 21:52:477555class JITIsolationTest : public IsolatedOriginTest,
7556 public ::testing::WithParamInterface<bool> {
7557 public:
7558 JITIsolationTest() = default;
7559
7560 ~JITIsolationTest() override = default;
7561
Scott Violet99861992023-02-08 01:20:127562 // A custom ContentBrowserTestContentBrowserClient to selectively turn off JIT
7563 // for certain sites.
7564 class JitContentBrowserClient
7565 : public ContentBrowserTestContentBrowserClient {
Will Harris2f439d0d2021-06-16 21:52:477566 public:
Scott Violet99861992023-02-08 01:20:127567 JitContentBrowserClient(bool jit_disabled_default,
7568 bool disable_site_isolation_entirely)
7569 : is_jit_disabled_by_default_(jit_disabled_default),
7570 is_site_isolation_disabled_entirely_(
7571 disable_site_isolation_entirely) {}
Will Harris2f439d0d2021-06-16 21:52:477572
Scott Violet99861992023-02-08 01:20:127573 bool IsJitDisabledForSite(BrowserContext* browser_context,
7574 const GURL& site_url) override {
7575 if (site_url.is_empty()) {
Will Harris2f439d0d2021-06-16 21:52:477576 return is_jit_disabled_by_default_;
7577 }
Sharon Yang01fdd492025-05-22 19:45:337578 if (site_url.DomainIs("jit-disabled.com") ||
7579 site_url.DomainIs("isolated.foo.com")) {
Scott Violet99861992023-02-08 01:20:127580 return true;
Will Harris2f439d0d2021-06-16 21:52:477581 }
Sharon Yang01fdd492025-05-22 19:45:337582 if (site_url.DomainIs("jit-enabled.com") ||
7583 site_url.DomainIs("isolated.bar.com")) {
Scott Violet99861992023-02-08 01:20:127584 return false;
7585 }
7586 return is_jit_disabled_by_default_;
Will Harris2f439d0d2021-06-16 21:52:477587 }
7588
Scott Violet99861992023-02-08 01:20:127589 bool ShouldEnableStrictSiteIsolation() override {
7590 return !is_site_isolation_disabled_entirely_;
Will Harris2f439d0d2021-06-16 21:52:477591 }
7592
7593 private:
Scott Violet99861992023-02-08 01:20:127594 bool is_jit_disabled_by_default_;
7595 bool is_site_isolation_disabled_entirely_;
Will Harris2f439d0d2021-06-16 21:52:477596 };
7597};
7598
7599IN_PROC_BROWSER_TEST_P(JITIsolationTest, MainFrameTest) {
7600 bool jit_disabled_by_default = GetParam();
Scott Violet99861992023-02-08 01:20:127601 JitContentBrowserClient policy(jit_disabled_by_default,
7602 /* disable_site_isolation_entirely */ false);
Will Harris2f439d0d2021-06-16 21:52:477603
Sharon Yang01fdd492025-05-22 19:45:337604 // Navigate to jit-disabled.com which should always have JIT disabled when
7605 // site isolation is enabled.
Will Harris2f439d0d2021-06-16 21:52:477606 GURL disabled_url(
7607 embedded_test_server()->GetURL("www.jit-disabled.com", "/title1.html"));
7608 EXPECT_TRUE(NavigateToURL(shell(), disabled_url));
7609
Sharon Yang01fdd492025-05-22 19:45:337610 if (AreAllSitesIsolatedForTesting()) {
7611 EXPECT_TRUE(shell()
7612 ->web_contents()
7613 ->GetPrimaryMainFrame()
7614 ->GetProcess()
7615 ->IsJitDisabled());
7616 } else {
7617 // Otherwise expect to use the default JIT value.
7618 EXPECT_EQ(jit_disabled_by_default, shell()
7619 ->web_contents()
7620 ->GetPrimaryMainFrame()
7621 ->GetProcess()
7622 ->IsJitDisabled());
7623 }
Will Harris2f439d0d2021-06-16 21:52:477624
Sharon Yang01fdd492025-05-22 19:45:337625 // Navigate to jit-enabled.com which should always have JIT enabled when site
7626 // isolation is enabled.
Will Harris2f439d0d2021-06-16 21:52:477627 GURL enabled_url(
7628 embedded_test_server()->GetURL("www.jit-enabled.com", "/title1.html"));
7629 EXPECT_TRUE(NavigateToURL(shell(), enabled_url));
7630
Sharon Yang01fdd492025-05-22 19:45:337631 if (AreAllSitesIsolatedForTesting()) {
7632 EXPECT_FALSE(shell()
7633 ->web_contents()
7634 ->GetPrimaryMainFrame()
7635 ->GetProcess()
7636 ->IsJitDisabled());
7637 } else {
7638 // Otherwise expect to use the default JIT value.
7639 EXPECT_EQ(jit_disabled_by_default, shell()
7640 ->web_contents()
7641 ->GetPrimaryMainFrame()
7642 ->GetProcess()
7643 ->IsJitDisabled());
7644 }
Will Harris2f439d0d2021-06-16 21:52:477645
7646 // Navigate to a site with no policy and it should match the default.
7647 GURL default_url(
7648 embedded_test_server()->GetURL("www.foo.com", "/title1.html"));
7649 EXPECT_TRUE(NavigateToURL(shell(), default_url));
7650
Dave Tapuska327c06c92022-06-13 20:31:517651 EXPECT_EQ(jit_disabled_by_default, shell()
7652 ->web_contents()
7653 ->GetPrimaryMainFrame()
7654 ->GetProcess()
7655 ->IsJitDisabled());
Will Harris2f439d0d2021-06-16 21:52:477656}
7657
7658IN_PROC_BROWSER_TEST_P(JITIsolationTest, DefaultSiteTest) {
Alex Moshchuk0c230b42021-10-15 01:37:307659 // Skip the test if --site-per-process is used on the command line, as the
7660 // test needs to run without strict site isolation (see
Scott Violet99861992023-02-08 01:20:127661 // JitContentBrowserClient below).
Jagadesh P1c8699c22023-11-13 14:09:377662 if (AreAllSitesIsolatedForTesting()) {
Alex Moshchuk0c230b42021-10-15 01:37:307663 return;
Jagadesh P1c8699c22023-11-13 14:09:377664 }
Alex Moshchuk0c230b42021-10-15 01:37:307665
Sharon Yang01fdd492025-05-22 19:45:337666 // Before adding a custom ContentBrowserClient, the initial empty document
7667 // should have JIT enabled by default.
7668 EXPECT_FALSE(shell()
7669 ->web_contents()
7670 ->GetPrimaryMainFrame()
7671 ->GetProcess()
7672 ->IsJitDisabled());
7673
Will Harris2f439d0d2021-06-16 21:52:477674 bool jit_disabled_by_default = GetParam();
Scott Violet99861992023-02-08 01:20:127675 JitContentBrowserClient policy(jit_disabled_by_default,
7676 /* disable_site_isolation_entirely */ true);
Will Harris2f439d0d2021-06-16 21:52:477677
7678 // All three sites should have JIT enabled or disabled together, if site
7679 // isolation is disabled, since they are all put into the default
Sharon Yang01fdd492025-05-22 19:45:337680 // SiteInstance or SiteInstanceGroup.
Will Harris2f439d0d2021-06-16 21:52:477681 GURL disabled_url(
7682 embedded_test_server()->GetURL("www.jit-disabled.com", "/title1.html"));
7683 EXPECT_TRUE(NavigateToURL(shell(), disabled_url));
7684
Dave Tapuska327c06c92022-06-13 20:31:517685 EXPECT_EQ(jit_disabled_by_default, shell()
7686 ->web_contents()
7687 ->GetPrimaryMainFrame()
7688 ->GetProcess()
7689 ->IsJitDisabled());
Will Harris2f439d0d2021-06-16 21:52:477690
7691 GURL enabled_url(
7692 embedded_test_server()->GetURL("www.jit-enabled.com", "/title1.html"));
7693 EXPECT_TRUE(NavigateToURL(shell(), enabled_url));
7694
Dave Tapuska327c06c92022-06-13 20:31:517695 EXPECT_EQ(jit_disabled_by_default, shell()
7696 ->web_contents()
7697 ->GetPrimaryMainFrame()
7698 ->GetProcess()
7699 ->IsJitDisabled());
Will Harris2f439d0d2021-06-16 21:52:477700
7701 GURL default_url(
7702 embedded_test_server()->GetURL("www.foo.com", "/title1.html"));
7703 EXPECT_TRUE(NavigateToURL(shell(), default_url));
7704
Dave Tapuska327c06c92022-06-13 20:31:517705 EXPECT_EQ(jit_disabled_by_default, shell()
7706 ->web_contents()
7707 ->GetPrimaryMainFrame()
7708 ->GetProcess()
7709 ->IsJitDisabled());
Sharon Yang01fdd492025-05-22 19:45:337710
7711 // isolated.foo.com and isolated.bar.com are explicitly isolated in
7712 // IsolatedOriginTest::SetUpCommandLine. They will still get their own
7713 // process, even with site isolation disabled. Since JIT is enabled or
7714 // disabled process wide, the isolated site's process can have a JIT enabled
7715 // value that differs from the default, which is specified by
7716 // JitContentBrowserClient::IsJitDisabledForSite.
7717 GURL foo_isolated_url(
7718 embedded_test_server()->GetURL("isolated.foo.com", "/title1.html"));
7719 EXPECT_TRUE(IsIsolatedOrigin(foo_isolated_url));
7720 EXPECT_TRUE(NavigateToURL(shell(), foo_isolated_url));
7721 EXPECT_TRUE(shell()
7722 ->web_contents()
7723 ->GetPrimaryMainFrame()
7724 ->GetProcess()
7725 ->IsJitDisabled());
7726
7727 GURL bar_isolated_url(
7728 embedded_test_server()->GetURL("isolated.bar.com", "/title1.html"));
7729 EXPECT_TRUE(IsIsolatedOrigin(bar_isolated_url));
7730 EXPECT_TRUE(NavigateToURL(shell(), bar_isolated_url));
7731 EXPECT_FALSE(shell()
7732 ->web_contents()
7733 ->GetPrimaryMainFrame()
7734 ->GetProcess()
7735 ->IsJitDisabled());
Will Harris2f439d0d2021-06-16 21:52:477736}
7737
7738INSTANTIATE_TEST_SUITE_P(JITEnabledByDefault,
7739 JITIsolationTest,
7740 ::testing::Values(false));
7741INSTANTIATE_TEST_SUITE_P(JITDisabledByDefault,
7742 JITIsolationTest,
7743 ::testing::Values(true));
7744
7745IN_PROC_BROWSER_TEST_F(JITIsolationTest, SubFrameTest) {
7746 // Set JIT to be enabled by default.
Scott Violet99861992023-02-08 01:20:127747 JitContentBrowserClient policy(
Will Harris2f439d0d2021-06-16 21:52:477748 /* jit_disabled_default */ false,
7749 /* disable_site_isolation_entirely */ false);
7750
7751 GURL default_embeds_disabled(embedded_test_server()->GetURL(
7752 "foo.com", "/cross_site_iframe_factory.html?foo.com(jit-disabled.com)"));
7753
7754 EXPECT_TRUE(NavigateToURL(shell(), default_embeds_disabled));
Dave Tapuska4eea4112021-09-16 15:49:217755 EXPECT_EQ(2u, CollectAllRenderFrameHosts(shell()->web_contents()).size());
Will Harris2f439d0d2021-06-16 21:52:477756
Carlos Caballero15caeeb2021-10-27 09:57:557757 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Will Harris2f439d0d2021-06-16 21:52:477758 FrameTreeNode* child_frame_node = root->child_at(0);
Sharon Yang01fdd492025-05-22 19:45:337759 if (AreAllSitesIsolatedForTesting()) {
7760 // Top frame 'foo.com' should have JIT enabled as that's the default.
7761 EXPECT_FALSE(root->current_frame_host()->GetProcess()->IsJitDisabled());
7762 // The frame containing jit-disabled.com should have JIT disabled.
7763 EXPECT_TRUE(
7764 child_frame_node->current_frame_host()->GetProcess()->IsJitDisabled());
7765 } else {
7766 // Without site isolation, both frames should have the default, which is JIT
7767 // enabled.
7768 EXPECT_FALSE(root->current_frame_host()->GetProcess()->IsJitDisabled());
7769 EXPECT_FALSE(
7770 child_frame_node->current_frame_host()->GetProcess()->IsJitDisabled());
7771 }
Will Harris2f439d0d2021-06-16 21:52:477772
7773 // And the other way round, where jit-disabled.com embeds foo.com.
7774 GURL disabled_embeds_default(embedded_test_server()->GetURL(
7775 "jit-disabled.com",
7776 "/cross_site_iframe_factory.html?jit-disabled.com(foo.com)"));
7777
7778 EXPECT_TRUE(NavigateToURL(shell(), disabled_embeds_default));
Dave Tapuska327c06c92022-06-13 20:31:517779 EXPECT_EQ(2u, CollectAllRenderFrameHosts(
7780 shell()->web_contents()->GetPrimaryMainFrame())
7781 .size());
Will Harris2f439d0d2021-06-16 21:52:477782
Carlos Caballero15caeeb2021-10-27 09:57:557783 root = web_contents()->GetPrimaryFrameTree().root();
Will Harris2f439d0d2021-06-16 21:52:477784 child_frame_node = root->child_at(0);
Sharon Yang01fdd492025-05-22 19:45:337785 if (AreAllSitesIsolatedForTesting()) {
7786 // Top frame 'jit-disabled.com' should have JIT disabled.
7787 EXPECT_TRUE(root->current_frame_host()->GetProcess()->IsJitDisabled());
7788 // The frame containing foo.com should have JIT enabled as that's the
7789 // default.
7790 EXPECT_FALSE(
7791 child_frame_node->current_frame_host()->GetProcess()->IsJitDisabled());
7792 } else {
7793 // Without site isolation, both frames should have the default, which is JIT
7794 // disabled.
7795 EXPECT_FALSE(root->current_frame_host()->GetProcess()->IsJitDisabled());
7796 EXPECT_FALSE(
7797 child_frame_node->current_frame_host()->GetProcess()->IsJitDisabled());
7798 }
Will Harris2f439d0d2021-06-16 21:52:477799}
7800
Alex Moshchuked2376d2022-05-05 22:38:467801// Check that jitless subframes obey process reuse policies.
7802IN_PROC_BROWSER_TEST_F(JITIsolationTest, SubFrameProcessReuse) {
7803 // Set JIT to be enabled by default.
Scott Violet99861992023-02-08 01:20:127804 JitContentBrowserClient policy(
Alex Moshchuked2376d2022-05-05 22:38:467805 /* jit_disabled_default */ false,
7806 /* disable_site_isolation_entirely */ false);
7807
7808 GURL default_embeds_disabled(embedded_test_server()->GetURL(
7809 "foo.com", "/cross_site_iframe_factory.html?foo.com(jit-disabled.com)"));
7810
7811 EXPECT_TRUE(NavigateToURL(shell(), default_embeds_disabled));
7812
Sharon Yang01fdd492025-05-22 19:45:337813 RenderFrameHostImpl* root =
7814 web_contents()->GetPrimaryFrameTree().root()->current_frame_host();
7815 RenderFrameHostImpl* child = web_contents()
7816 ->GetPrimaryFrameTree()
7817 .root()
7818 ->child_at(0)
7819 ->current_frame_host();
7820 if (AreAllSitesIsolatedForTesting()) {
7821 // Top frame 'foo.com' should have JIT enabled as that's the default.
7822 EXPECT_FALSE(root->GetProcess()->IsJitDisabled());
7823 // The frame containing jit-disabled.com should have JIT disabled.
7824 EXPECT_TRUE(child->GetProcess()->IsJitDisabled());
7825 } else {
7826 EXPECT_EQ(root->GetProcess(), child->GetProcess());
7827 // Without site isolation, both frames should have the default JIT value,
7828 // which is enabled.
7829 EXPECT_FALSE(root->GetProcess()->IsJitDisabled());
7830 EXPECT_FALSE(child->GetProcess()->IsJitDisabled());
7831 }
Alex Moshchuked2376d2022-05-05 22:38:467832
7833 // Create a new window, unrelated to the current one, and set up the same
7834 // frame hierarchy.
7835 Shell* new_shell = CreateBrowser();
7836 EXPECT_TRUE(NavigateToURL(new_shell, default_embeds_disabled));
7837
Sharon Yang01fdd492025-05-22 19:45:337838 FrameTreeNode* new_root_node =
Alex Moshchuked2376d2022-05-05 22:38:467839 static_cast<WebContentsImpl*>(new_shell->web_contents())
7840 ->GetPrimaryFrameTree()
7841 .root();
Sharon Yang01fdd492025-05-22 19:45:337842 RenderFrameHostImpl* new_root = new_root_node->current_frame_host();
7843 RenderFrameHostImpl* new_child = new_root->child_at(0)->current_frame_host();
Alex Moshchuked2376d2022-05-05 22:38:467844
7845 // The subframes should be in separate BrowsingInstances, but because they
Sharon Yang01fdd492025-05-22 19:45:337846 // have the same site, they should share the same process with site isolation.
7847 // Without site isolation, they will be in the corresponding
7848 // BrowsingInstance's default SiteInstance/Group, and will have different
7849 // processes.
7850 EXPECT_FALSE(new_child->GetSiteInstance()->IsRelatedSiteInstance(
7851 child->GetSiteInstance()));
7852 if (AreAllSitesIsolatedForTesting()) {
7853 EXPECT_FALSE(new_root->GetProcess()->IsJitDisabled());
7854 EXPECT_TRUE(new_child->GetProcess()->IsJitDisabled());
7855 EXPECT_EQ(new_child->GetProcess(), child->GetProcess());
7856 } else {
7857 EXPECT_FALSE(new_root->GetProcess()->IsJitDisabled());
7858 EXPECT_FALSE(new_child->GetProcess()->IsJitDisabled());
7859 EXPECT_EQ(new_root->GetProcess(), new_child->GetProcess());
7860 EXPECT_NE(new_child->GetProcess(), child->GetProcess());
7861 }
Alex Moshchuked2376d2022-05-05 22:38:467862}
7863
Sharon Yang1a7a63702025-05-23 01:14:557864INSTANTIATE_TEST_SUITE_P(
7865 All,
7866 IsolatedOriginTestWithDefaultSiteInstanceGroups,
7867 ::testing::Bool(),
7868 &IsolatedOriginTestWithDefaultSiteInstanceGroups::DescribeParams);
alexmos3b9ad102017-05-26 23:41:087869} // namespace content