1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <sstream>
6 #include <vector>
7
8 #include "base/bind.h"
9 #include "base/command_line.h"
10 #include "base/macros.h"
11 #include "base/strings/string_util.h"
12 #include "base/test/scoped_feature_list.h"
13 #include "build/build_config.h"
14 #include "components/network_session_configurator/common/network_switches.h"
15 #include "content/browser/bad_message.h"
16 #include "content/browser/child_process_security_policy_impl.h"
17 #include "content/browser/renderer_host/render_process_host_impl.h"
18 #include "content/browser/storage_partition_impl.h"
19 #include "content/browser/web_contents/web_contents_impl.h"
20 #include "content/public/browser/browser_or_resource_context.h"
21 #include "content/public/browser/render_frame_host.h"
22 #include "content/public/browser/render_process_host.h"
23 #include "content/public/browser/site_isolation_policy.h"
24 #include "content/public/common/content_features.h"
25 #include "content/public/common/content_switches.h"
26 #include "content/public/test/browser_test_utils.h"
27 #include "content/public/test/content_browser_test.h"
28 #include "content/public/test/content_browser_test_utils.h"
29 #include "content/public/test/navigation_handle_observer.h"
30 #include "content/public/test/test_frame_navigation_observer.h"
31 #include "content/public/test/test_navigation_observer.h"
32 #include "content/public/test/test_utils.h"
33 #include "content/shell/browser/shell.h"
34 #include "content/test/content_browser_test_utils_internal.h"
35 #include "mojo/public/cpp/bindings/pending_receiver.h"
36 #include "mojo/public/cpp/bindings/receiver_set.h"
37 #include "net/dns/mock_host_resolver.h"
38 #include "net/test/embedded_test_server/embedded_test_server.h"
39 #include "net/test/embedded_test_server/http_request.h"
40 #include "net/test/embedded_test_server/http_response.h"
41 #include "services/network/public/cpp/features.h"
42 #include "third_party/blink/public/common/features.h"
43 #include "third_party/blink/public/mojom/broadcastchannel/broadcast_channel.mojom-test-utils.h"
44 #include "third_party/blink/public/mojom/broadcastchannel/broadcast_channel.mojom.h"
45 #include "third_party/blink/public/mojom/dom_storage/dom_storage.mojom-test-utils.h"
46 #include "url/gurl.h"
47
48 namespace content {
49
50 using IsolatedOriginSource = ChildProcessSecurityPolicy::IsolatedOriginSource;
51
52 // This is a base class for all tests in this class. It does not isolate any
53 // origins and only provides common helper functions to the other test classes.
54 class IsolatedOriginTestBase : public ContentBrowserTest {
55 public:
IsolatedOriginTestBase()56 IsolatedOriginTestBase() {}
~IsolatedOriginTestBase()57 ~IsolatedOriginTestBase() override {}
58
59 // Check if |origin| is an isolated origin. This helper is used in tests
60 // that care only about globally applicable isolated origins (not restricted
61 // to a particular BrowsingInstance or profile).
IsIsolatedOrigin(const url::Origin & origin)62 bool IsIsolatedOrigin(const url::Origin& origin) {
63 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
64 IsolationContext isolation_context(
65 shell()->web_contents()->GetBrowserContext());
66 return policy->IsIsolatedOrigin(isolation_context, origin);
67 }
68
IsIsolatedOrigin(const GURL & url)69 bool IsIsolatedOrigin(const GURL& url) {
70 return IsIsolatedOrigin(url::Origin::Create(url));
71 }
72
web_contents() const73 WebContentsImpl* web_contents() const {
74 return static_cast<WebContentsImpl*>(shell()->web_contents());
75 }
76
77 private:
78 DISALLOW_COPY_AND_ASSIGN(IsolatedOriginTestBase);
79 };
80
81 class IsolatedOriginTest : public IsolatedOriginTestBase {
82 public:
IsolatedOriginTest()83 IsolatedOriginTest() {}
~IsolatedOriginTest()84 ~IsolatedOriginTest() override {}
85
SetUpCommandLine(base::CommandLine * command_line)86 void SetUpCommandLine(base::CommandLine* command_line) override {
87 ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
88
89 std::string origin_list =
90 embedded_test_server()->GetURL("isolated.foo.com", "/").spec() + "," +
91 embedded_test_server()->GetURL("isolated.bar.com", "/").spec();
92 command_line->AppendSwitchASCII(switches::kIsolateOrigins, origin_list);
93 }
94
SetUpOnMainThread()95 void SetUpOnMainThread() override {
96 host_resolver()->AddRule("*", "127.0.0.1");
97 embedded_test_server()->StartAcceptingConnections();
98 }
99
InjectAndClickLinkTo(GURL url)100 void InjectAndClickLinkTo(GURL url) {
101 EXPECT_TRUE(ExecuteScript(web_contents(),
102 "var link = document.createElement('a');"
103 "link.href = '" +
104 url.spec() +
105 "';"
106 "document.body.appendChild(link);"
107 "link.click();"));
108 }
109
110 private:
111 DISALLOW_COPY_AND_ASSIGN(IsolatedOriginTest);
112 };
113
114 class OriginIsolationOptInTest : public IsolatedOriginTestBase {
115 public:
OriginIsolationOptInTest()116 OriginIsolationOptInTest()
117 : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
118 ~OriginIsolationOptInTest() override = default;
119
SetUpCommandLine(base::CommandLine * command_line)120 void SetUpCommandLine(base::CommandLine* command_line) override {
121 IsolatedOriginTestBase::SetUpCommandLine(command_line);
122 ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
123
124 // This is needed for this test to run properly on platforms where
125 // --site-per-process isn't the default, such as Android.
126 IsolateAllSitesForTesting(command_line);
127 command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
128 feature_list_.InitAndEnableFeature(features::kOriginPolicy);
129 }
130
SetUpOnMainThread()131 void SetUpOnMainThread() override {
132 IsolatedOriginTestBase::SetUpOnMainThread();
133 https_server()->AddDefaultHandlers(GetTestDataFilePath());
134 https_server()->RegisterRequestHandler(base::BindRepeating(
135 &OriginIsolationOptInTest::HandleResponse, base::Unretained(this)));
136 ASSERT_TRUE(https_server()->Start());
137 host_resolver()->AddRule("*", "127.0.0.1");
138 embedded_test_server()->StartAcceptingConnections();
139 }
140
TearDownOnMainThread()141 void TearDownOnMainThread() override {
142 ASSERT_TRUE(https_server()->ShutdownAndWaitUntilComplete());
143 IsolatedOriginTestBase::TearDownOnMainThread();
144 }
145
SetOriginPolicyManifest(const std::string & manifest)146 void SetOriginPolicyManifest(const std::string& manifest) {
147 origin_policy_manifest_ = manifest;
148 }
149
150 // Need an https server because
151 // OriginPolicyThrottle::ShouldRequestOriginPolicy() will return false
152 // otherwise.
https_server()153 net::EmbeddedTestServer* https_server() { return &https_server_; }
154
155 private:
HandleResponse(const net::test_server::HttpRequest & request)156 std::unique_ptr<net::test_server::HttpResponse> HandleResponse(
157 const net::test_server::HttpRequest& request) {
158 std::unique_ptr<net::test_server::BasicHttpResponse> response =
159 std::make_unique<net::test_server::BasicHttpResponse>();
160
161 // TODO(wjmaclean): document how this handler works.
162 if (request.relative_url == "/isolate_me") {
163 response->set_code(net::HTTP_OK);
164 response->set_content_type("text/html");
165 response->AddCustomHeader("Origin-Policy", "allowed=(latest)");
166 response->set_content("isolate me!");
167 return std::move(response);
168 }
169
170 // Intercepts the request to get the origin policy, and injects the policy.
171 // Note: this will only be activated for requests that load "isolate_me"
172 // above, since only it sets the Origin-Policy header.
173 if (request.relative_url == "/.well-known/origin-policy") {
174 response->set_code(net::HTTP_OK);
175 response->set_content(origin_policy_manifest_);
176 return std::move(response);
177 }
178
179 // If we return nullptr, then the server will go ahead and actually serve
180 // the file.
181 return std::unique_ptr<net::test_server::HttpResponse>();
182 }
183
184 net::EmbeddedTestServer https_server_;
185 std::string origin_policy_manifest_;
186 base::test::ScopedFeatureList feature_list_;
187
188 DISALLOW_COPY_AND_ASSIGN(OriginIsolationOptInTest);
189 };
190
191 // In this test the sub-origin is isolated because the origin policy requests
192 // "isolation". It will have a different site instance than the main frame.
IN_PROC_BROWSER_TEST_F(OriginIsolationOptInTest,SimpleSubOriginIsolationTest)193 IN_PROC_BROWSER_TEST_F(OriginIsolationOptInTest, SimpleSubOriginIsolationTest) {
194 SetOriginPolicyManifest(R"({ "ids": ["my-policy"], "isolation": true })");
195 // Start off with an a(a) page, then navigate the subframe to an isolated sub
196 // origin.
197 GURL test_url(https_server()->GetURL("foo.com",
198 "/cross_site_iframe_factory.html?"
199 "foo.com(foo.com)"));
200 GURL isolated_sub_origin(
201 https_server()->GetURL("isolated.foo.com", "/isolate_me"));
202 EXPECT_TRUE(NavigateToURL(shell(), test_url));
203 EXPECT_EQ(2u, shell()->web_contents()->GetAllFrames().size());
204
205 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
206 FrameTreeNode* child_frame_node = root->child_at(0);
207 NavigateFrameToURL(child_frame_node, isolated_sub_origin);
208 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
209 child_frame_node->current_frame_host()->GetSiteInstance());
210 EXPECT_TRUE(child_frame_node->current_frame_host()
211 ->GetSiteInstance()
212 ->RequiresDedicatedProcess());
213 GURL expected_isolated_sub_origin =
214 https_server()->GetURL("isolated.foo.com", "/");
215 EXPECT_EQ(
216 expected_isolated_sub_origin,
217 child_frame_node->current_frame_host()->GetSiteInstance()->GetSiteURL());
218 EXPECT_EQ(expected_isolated_sub_origin,
219 ChildProcessSecurityPolicyImpl::GetInstance()->GetOriginLock(
220 child_frame_node->current_frame_host()->GetProcess()->GetID()));
221 }
222
223 // In this test the sub-origin isn't isolated because the origin policy doesn't
224 // request "isolation". It will have the same site instance as the main frame.
IN_PROC_BROWSER_TEST_F(OriginIsolationOptInTest,SimpleSubOriginNonIsolationTest)225 IN_PROC_BROWSER_TEST_F(OriginIsolationOptInTest,
226 SimpleSubOriginNonIsolationTest) {
227 SetOriginPolicyManifest(R"({ "ids": ["my-policy"] })");
228 // Start off with an a(a) page, then navigate the subframe to an isolated sub
229 // origin.
230 GURL test_url(https_server()->GetURL("foo.com",
231 "/cross_site_iframe_factory.html?"
232 "foo.com(foo.com)"));
233 GURL isolated_sub_origin(
234 https_server()->GetURL("isolated.foo.com", "/isolate_me"));
235 EXPECT_TRUE(NavigateToURL(shell(), test_url));
236 EXPECT_EQ(2u, shell()->web_contents()->GetAllFrames().size());
237
238 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
239 FrameTreeNode* child_frame_node = root->child_at(0);
240 NavigateFrameToURL(child_frame_node, isolated_sub_origin);
241 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
242 child_frame_node->current_frame_host()->GetSiteInstance());
243 }
244
245 // This test verifies that renderer-initiated navigations to/from isolated
246 // sub-origins works as expected.
IN_PROC_BROWSER_TEST_F(OriginIsolationOptInTest,RendererInitiatedNavigations)247 IN_PROC_BROWSER_TEST_F(OriginIsolationOptInTest, RendererInitiatedNavigations) {
248 SetOriginPolicyManifest(R"({ "ids": ["my-policy"], "isolation": true })");
249 GURL test_url(https_server()->GetURL("foo.com",
250 "/cross_site_iframe_factory.html?"
251 "foo.com(foo.com)"));
252 EXPECT_TRUE(NavigateToURL(shell(), test_url));
253 EXPECT_EQ(2u, shell()->web_contents()->GetAllFrames().size());
254
255 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
256 FrameTreeNode* child = root->child_at(0);
257
258 GURL isolated_sub_origin_url(
259 https_server()->GetURL("isolated.foo.com", "/isolate_me"));
260 {
261 // Navigate the child to an isolated origin.
262 TestFrameNavigationObserver observer(child);
263 EXPECT_TRUE(ExecuteScript(
264 child, "location.href = '" + isolated_sub_origin_url.spec() + "';"));
265 observer.Wait();
266 }
267 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
268 child->current_frame_host()->GetSiteInstance());
269
270 GURL non_isolated_sub_origin_url(
271 https_server()->GetURL("bar.foo.com", "/title1.html"));
272 {
273 // Navigate the child to a non-isolated origin.
274 TestFrameNavigationObserver observer(child);
275 EXPECT_TRUE(ExecuteScript(
276 child,
277 "location.href = '" + non_isolated_sub_origin_url.spec() + "';"));
278 observer.Wait();
279 }
280 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
281 child->current_frame_host()->GetSiteInstance());
282 }
283
284 // Check that navigating a main frame from an non-isolated origin to an
285 // isolated origin and vice versa swaps processes and uses a new SiteInstance,
286 // both for renderer-initiated and browser-initiated navigations.
287 // Note: this test is essentially identical to
288 // IsolatedOriginTest.MainFrameNavigation.
IN_PROC_BROWSER_TEST_F(OriginIsolationOptInTest,MainFrameNavigation)289 IN_PROC_BROWSER_TEST_F(OriginIsolationOptInTest, MainFrameNavigation) {
290 SetOriginPolicyManifest(R"({ "ids": ["my-policy"], "isolation": true })");
291 GURL unisolated_url(https_server()->GetURL("www.foo.com", "/title1.html"));
292 GURL isolated_url(https_server()->GetURL("isolated.foo.com", "/isolate_me"));
293
294 EXPECT_TRUE(NavigateToURL(shell(), unisolated_url));
295
296 // Open a same-site popup to keep the www.foo.com process alive.
297 Shell* popup = OpenPopup(shell(), GURL(url::kAboutBlankURL), "foo");
298 SiteInstance* unisolated_instance =
299 popup->web_contents()->GetMainFrame()->GetSiteInstance();
300 RenderProcessHost* unisolated_process =
301 popup->web_contents()->GetMainFrame()->GetProcess();
302
303 // Go to isolated.foo.com with a renderer-initiated navigation.
304 EXPECT_TRUE(NavigateToURLFromRenderer(web_contents(), isolated_url));
305 scoped_refptr<SiteInstance> isolated_instance =
306 web_contents()->GetSiteInstance();
307 EXPECT_EQ(isolated_instance, web_contents()->GetSiteInstance());
308 EXPECT_NE(unisolated_process, web_contents()->GetMainFrame()->GetProcess());
309
310 // The site URL for isolated.foo.com should be the full origin rather than
311 // scheme and eTLD+1.
312 EXPECT_EQ(https_server()->GetURL("isolated.foo.com", "/"),
313 isolated_instance->GetSiteURL());
314
315 // Now use a renderer-initiated navigation to go to an unisolated origin,
316 // www.foo.com. This should end up back in the |popup|'s process.
317 EXPECT_TRUE(NavigateToURLFromRenderer(web_contents(), unisolated_url));
318 EXPECT_EQ(unisolated_instance, web_contents()->GetSiteInstance());
319 EXPECT_EQ(unisolated_process, web_contents()->GetMainFrame()->GetProcess());
320
321 // Now, perform a browser-initiated navigation to an isolated origin and
322 // ensure that this ends up in a new process and SiteInstance for
323 // isolated.foo.com.
324 EXPECT_TRUE(NavigateToURL(shell(), isolated_url));
325 EXPECT_NE(web_contents()->GetSiteInstance(), unisolated_instance);
326 EXPECT_NE(web_contents()->GetMainFrame()->GetProcess(), unisolated_process);
327
328 // Go back to www.foo.com: this should end up in the unisolated process.
329 {
330 TestNavigationObserver back_observer(web_contents());
331 web_contents()->GetController().GoBack();
332 back_observer.Wait();
333 }
334
335 EXPECT_EQ(unisolated_instance, web_contents()->GetSiteInstance());
336 EXPECT_EQ(unisolated_process, web_contents()->GetMainFrame()->GetProcess());
337
338 // Go back again. This should go to isolated.foo.com in an isolated process.
339 {
340 TestNavigationObserver back_observer(web_contents());
341 web_contents()->GetController().GoBack();
342 back_observer.Wait();
343 }
344
345 EXPECT_EQ(isolated_instance, web_contents()->GetSiteInstance());
346 EXPECT_NE(unisolated_process, web_contents()->GetMainFrame()->GetProcess());
347
348 // Do a renderer-initiated navigation from isolated.foo.com to another
349 // isolated origin and ensure there is a different isolated process.
350 GURL second_isolated_url(
351 https_server()->GetURL("isolated.bar.com", "/isolate_me"));
352 EXPECT_TRUE(NavigateToURLFromRenderer(web_contents(), second_isolated_url));
353 EXPECT_EQ(https_server()->GetURL("isolated.bar.com", "/"),
354 web_contents()->GetSiteInstance()->GetSiteURL());
355 EXPECT_NE(isolated_instance, web_contents()->GetSiteInstance());
356 EXPECT_NE(unisolated_instance, web_contents()->GetSiteInstance());
357 }
358
359 // This test ensures that if an origin starts off being isolated in a
360 // BrowsingInstance, it continues that way within the BrowsingInstance, even
361 // if a new policy is received that removes the opt-in request.
IN_PROC_BROWSER_TEST_F(OriginIsolationOptInTest,OriginIsolationStateRetainedForBrowsingInstance)362 IN_PROC_BROWSER_TEST_F(OriginIsolationOptInTest,
363 OriginIsolationStateRetainedForBrowsingInstance) {
364 SetOriginPolicyManifest(R"({ "ids": ["my-policy"], "isolation": true })");
365 // Start off with an a(a,a) page, then navigate the subframe to an isolated
366 // sub origin.
367 GURL test_url(https_server()->GetURL("foo.com",
368 "/cross_site_iframe_factory.html?"
369 "foo.com(foo.com, foo.com)"));
370 GURL isolated_sub_origin(
371 https_server()->GetURL("isolated.foo.com", "/isolate_me"));
372 EXPECT_TRUE(NavigateToURL(shell(), test_url));
373 EXPECT_EQ(3u, shell()->web_contents()->GetAllFrames().size());
374
375 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
376 FrameTreeNode* child_frame_node0 = root->child_at(0);
377 FrameTreeNode* child_frame_node1 = root->child_at(1);
378
379 NavigateFrameToURL(child_frame_node0, isolated_sub_origin);
380 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
381 child_frame_node0->current_frame_host()->GetSiteInstance());
382
383 // Change OriginPolicy manifest to stop isolating the sub-origin. It should
384 // still be isolated, to remain consistent with the other frame.
385 SetOriginPolicyManifest(R"({ })");
386 NavigateFrameToURL(child_frame_node1, isolated_sub_origin);
387 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
388 child_frame_node1->current_frame_host()->GetSiteInstance());
389
390 // The two sub-frames should be in the same site instance.
391 EXPECT_EQ(child_frame_node0->current_frame_host()->GetSiteInstance(),
392 child_frame_node1->current_frame_host()->GetSiteInstance());
393
394 // Make sure the master opt-in list no longer has the origin listed.
395 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
396 EXPECT_FALSE(policy->DoesOriginRequestOptInIsolation(
397 IsolationContext(shell()->web_contents()->GetBrowserContext()),
398 url::Origin::Create(isolated_sub_origin)));
399 }
400
401 // This test ensures that if an origin starts off not being isolated in a
402 // BrowsingInstance, it continues that way within the BrowsingInstance, even
403 // if a new opt-in policy is received.
404 // TODO(wjmaclean): Re-enable this once we support tracking non-opted-in
405 // origins.
IN_PROC_BROWSER_TEST_F(OriginIsolationOptInTest,DISABLED_OriginNonIsolationStateRetainedForBrowsingInstance)406 IN_PROC_BROWSER_TEST_F(
407 OriginIsolationOptInTest,
408 DISABLED_OriginNonIsolationStateRetainedForBrowsingInstance) {
409 SetOriginPolicyManifest(R"({ "ids": ["my-policy"] })");
410 // Start off with an a(a,a) page, then navigate the subframe to an isolated
411 // sub origin.
412 GURL test_url(https_server()->GetURL("foo.com",
413 "/cross_site_iframe_factory.html?"
414 "foo.com(foo.com, foo.com)"));
415 GURL isolated_sub_origin(
416 https_server()->GetURL("isolated.foo.com", "/isolate_me"));
417 EXPECT_TRUE(NavigateToURL(shell(), test_url));
418 EXPECT_EQ(3u, shell()->web_contents()->GetAllFrames().size());
419
420 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
421 FrameTreeNode* child_frame_node0 = root->child_at(0);
422 FrameTreeNode* child_frame_node1 = root->child_at(1);
423
424 NavigateFrameToURL(child_frame_node0, isolated_sub_origin);
425 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
426 child_frame_node0->current_frame_host()->GetSiteInstance());
427
428 // Change OriginPolicy manifest to start isolating the sub-origin. It should
429 // still be isolated, to remain consistent with the other frame.
430 SetOriginPolicyManifest(R"({ "ids": ["my-policy"], "isolation": true })");
431 NavigateFrameToURL(child_frame_node1, isolated_sub_origin);
432 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
433 child_frame_node1->current_frame_host()->GetSiteInstance());
434
435 // Make sure the master opt-in list has the origin listed.
436 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
437 EXPECT_TRUE(policy->DoesOriginRequestOptInIsolation(
438 IsolationContext(shell()->web_contents()->GetBrowserContext()),
439 url::Origin::Create(isolated_sub_origin)));
440 }
441
442 // This test handles the case where the base origin is isolated, but a
443 // sub-origin isn't. In this case we still need to isolate the sub-origin to
444 // respect the base-origin's isolation request.
445 // TODO(wjmaclean): Modify this to verify that the sub-origin is placed into the
446 // site-keyed SiteInstance corresponding to the base-origin, and not the
447 // origin-keyed SiteInstance the base origin is assigned to.
IN_PROC_BROWSER_TEST_F(OriginIsolationOptInTest,IsolatedBaseOrigin)448 IN_PROC_BROWSER_TEST_F(OriginIsolationOptInTest, IsolatedBaseOrigin) {
449 SetOriginPolicyManifest(R"({ "ids": ["my-policy"], "isolation": true })");
450 // Start off with an isolated base-origin in an a(a) configuration, then
451 // navigate the subframe to a sub-origin no requesting isolation.
452 GURL test_url(https_server()->GetURL(
453 "foo.com", "/isolated_base_origin_with_subframe.html"));
454 GURL non_isolated_sub_origin(
455 https_server()->GetURL("non_isolated.foo.com", "/title1.html"));
456 EXPECT_TRUE(NavigateToURL(shell(), test_url));
457 EXPECT_EQ(2u, shell()->web_contents()->GetAllFrames().size());
458
459 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
460 FrameTreeNode* child_frame_node = root->child_at(0);
461 NavigateFrameToURL(child_frame_node, non_isolated_sub_origin);
462 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
463 child_frame_node->current_frame_host()->GetSiteInstance());
464 // Make sure the master opt-in list has both the base origin and the sub
465 // origin both isolated.
466 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
467 EXPECT_TRUE(policy->DoesOriginRequestOptInIsolation(
468 IsolationContext(shell()->web_contents()->GetBrowserContext()),
469 url::Origin::Create(test_url)));
470 EXPECT_FALSE(policy->DoesOriginRequestOptInIsolation(
471 IsolationContext(shell()->web_contents()->GetBrowserContext()),
472 url::Origin::Create(non_isolated_sub_origin)));
473 }
474
475 class StrictOriginIsolationTest : public IsolatedOriginTestBase {
476 public:
StrictOriginIsolationTest()477 StrictOriginIsolationTest() {}
~StrictOriginIsolationTest()478 ~StrictOriginIsolationTest() override {}
479
SetUpCommandLine(base::CommandLine * command_line)480 void SetUpCommandLine(base::CommandLine* command_line) override {
481 IsolatedOriginTestBase::SetUpCommandLine(command_line);
482 ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
483
484 // This is needed for this test to run properly on platforms where
485 // --site-per-process isn't the default, such as Android.
486 IsolateAllSitesForTesting(command_line);
487 feature_list_.InitAndEnableFeature(features::kStrictOriginIsolation);
488 }
489
SetUpOnMainThread()490 void SetUpOnMainThread() override {
491 host_resolver()->AddRule("*", "127.0.0.1");
492 embedded_test_server()->StartAcceptingConnections();
493 }
494
495 private:
496 base::test::ScopedFeatureList feature_list_;
497
498 DISALLOW_COPY_AND_ASSIGN(StrictOriginIsolationTest);
499 };
500
IN_PROC_BROWSER_TEST_F(StrictOriginIsolationTest,SubframesAreIsolated)501 IN_PROC_BROWSER_TEST_F(StrictOriginIsolationTest, SubframesAreIsolated) {
502 GURL test_url(embedded_test_server()->GetURL(
503 "foo.com",
504 "/cross_site_iframe_factory.html?"
505 "foo.com(mail.foo.com,bar.foo.com(foo.com),foo.com)"));
506 EXPECT_TRUE(NavigateToURL(shell(), test_url));
507 EXPECT_EQ(5u, shell()->web_contents()->GetAllFrames().size());
508
509 // Make sure we have three separate processes.
510 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
511 RenderFrameHost* main_frame = root->current_frame_host();
512 int main_frame_id = main_frame->GetProcess()->GetID();
513 RenderFrameHost* child_frame0 = root->child_at(0)->current_frame_host();
514 int child_frame0_id = child_frame0->GetProcess()->GetID();
515 RenderFrameHost* child_frame1 = root->child_at(1)->current_frame_host();
516 int child_frame1_id = child_frame1->GetProcess()->GetID();
517 RenderFrameHost* child_frame2 = root->child_at(2)->current_frame_host();
518 int child_frame2_id = child_frame2->GetProcess()->GetID();
519 RenderFrameHost* grandchild_frame0 =
520 root->child_at(1)->child_at(0)->current_frame_host();
521 int grandchild_frame0_id = grandchild_frame0->GetProcess()->GetID();
522 EXPECT_NE(main_frame_id, child_frame0_id);
523 EXPECT_NE(main_frame_id, child_frame1_id);
524 EXPECT_EQ(main_frame_id, child_frame2_id);
525 EXPECT_EQ(main_frame_id, grandchild_frame0_id);
526
527 std::string port_string =
528 base::StringPrintf(":%u", embedded_test_server()->port());
529 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
530 EXPECT_EQ(GURL("http://foo.com" + port_string),
531 policy->GetOriginLock(main_frame_id));
532 EXPECT_EQ(GURL("http://mail.foo.com" + port_string),
533 policy->GetOriginLock(child_frame0_id));
534 EXPECT_EQ(GURL("http://bar.foo.com" + port_string),
535 policy->GetOriginLock(child_frame1_id));
536 EXPECT_EQ(GURL("http://foo.com" + port_string),
537 policy->GetOriginLock(child_frame2_id));
538 EXPECT_EQ(GURL("http://foo.com" + port_string),
539 policy->GetOriginLock(grandchild_frame0_id));
540
541 // Navigate child_frame1 to a new origin ... it should get its own process.
542 FrameTreeNode* child_frame2_node = root->child_at(2);
543 GURL foo_url(embedded_test_server()->GetURL("www.foo.com", "/title1.html"));
544 NavigateFrameToURL(child_frame2_node, foo_url);
545 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
546 child_frame2_node->current_frame_host()->GetSiteInstance());
547 // The old RenderFrameHost for subframe3 will no longer be valid, so get the
548 // new one.
549 child_frame2 = root->child_at(2)->current_frame_host();
550 EXPECT_NE(main_frame->GetProcess()->GetID(),
551 child_frame2->GetProcess()->GetID());
552 EXPECT_EQ(GURL("http://www.foo.com" + port_string),
553 policy->GetOriginLock(child_frame2->GetProcess()->GetID()));
554 }
555
IN_PROC_BROWSER_TEST_F(StrictOriginIsolationTest,MainframesAreIsolated)556 IN_PROC_BROWSER_TEST_F(StrictOriginIsolationTest, MainframesAreIsolated) {
557 GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
558 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
559 EXPECT_EQ(1u, web_contents()->GetAllFrames().size());
560 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
561
562 auto foo_process_id = web_contents()->GetMainFrame()->GetProcess()->GetID();
563 SiteInstance* foo_site_instance = shell()->web_contents()->GetSiteInstance();
564 EXPECT_EQ(foo_site_instance->GetSiteURL(),
565 policy->GetOriginLock(foo_process_id));
566
567 EXPECT_TRUE(NavigateToURL(
568 shell(), embedded_test_server()->GetURL("sub.foo.com", "/title1.html")));
569 auto sub_foo_process_id =
570 shell()->web_contents()->GetMainFrame()->GetProcess()->GetID();
571 SiteInstance* sub_foo_site_instance =
572 shell()->web_contents()->GetSiteInstance();
573 EXPECT_EQ(sub_foo_site_instance->GetSiteURL(),
574 policy->GetOriginLock(sub_foo_process_id));
575
576 EXPECT_NE(foo_process_id, sub_foo_process_id);
577 EXPECT_NE(foo_site_instance->GetSiteURL(),
578 sub_foo_site_instance->GetSiteURL());
579
580 // Now verify with a renderer-initiated navigation.
581 GURL another_foo_url(
582 embedded_test_server()->GetURL("another.foo.com", "/title2.html"));
583 EXPECT_TRUE(NavigateToURLFromRenderer(shell(), another_foo_url));
584 auto another_foo_process_id =
585 shell()->web_contents()->GetMainFrame()->GetProcess()->GetID();
586 SiteInstance* another_foo_site_instance =
587 shell()->web_contents()->GetSiteInstance();
588 EXPECT_NE(another_foo_process_id, sub_foo_process_id);
589 EXPECT_NE(another_foo_process_id, foo_process_id);
590 EXPECT_EQ(another_foo_site_instance->GetSiteURL(),
591 policy->GetOriginLock(another_foo_process_id));
592 EXPECT_NE(another_foo_site_instance, foo_site_instance);
593 }
594
595 // Check that navigating a main frame from an non-isolated origin to an
596 // isolated origin and vice versa swaps processes and uses a new SiteInstance,
597 // both for renderer-initiated and browser-initiated navigations.
IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,MainFrameNavigation)598 IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, MainFrameNavigation) {
599 GURL unisolated_url(
600 embedded_test_server()->GetURL("www.foo.com", "/title1.html"));
601 GURL isolated_url(
602 embedded_test_server()->GetURL("isolated.foo.com", "/title2.html"));
603
604 EXPECT_TRUE(NavigateToURL(shell(), unisolated_url));
605
606 // Open a same-site popup to keep the www.foo.com process alive.
607 Shell* popup = OpenPopup(shell(), GURL(url::kAboutBlankURL), "foo");
608 SiteInstance* unisolated_instance =
609 popup->web_contents()->GetMainFrame()->GetSiteInstance();
610 RenderProcessHost* unisolated_process =
611 popup->web_contents()->GetMainFrame()->GetProcess();
612
613 // Go to isolated.foo.com with a renderer-initiated navigation.
614 EXPECT_TRUE(NavigateToURLFromRenderer(web_contents(), isolated_url));
615 scoped_refptr<SiteInstance> isolated_instance =
616 web_contents()->GetSiteInstance();
617 EXPECT_EQ(isolated_instance, web_contents()->GetSiteInstance());
618 EXPECT_NE(unisolated_process, web_contents()->GetMainFrame()->GetProcess());
619
620 // The site URL for isolated.foo.com should be the full origin rather than
621 // scheme and eTLD+1.
622 EXPECT_EQ(GURL("http://isolated.foo.com/"), isolated_instance->GetSiteURL());
623
624 // Now use a renderer-initiated navigation to go to an unisolated origin,
625 // www.foo.com. This should end up back in the |popup|'s process.
626 EXPECT_TRUE(NavigateToURLFromRenderer(web_contents(), unisolated_url));
627 EXPECT_EQ(unisolated_instance, web_contents()->GetSiteInstance());
628 EXPECT_EQ(unisolated_process, web_contents()->GetMainFrame()->GetProcess());
629
630 // Now, perform a browser-initiated navigation to an isolated origin and
631 // ensure that this ends up in a new process and SiteInstance for
632 // isolated.foo.com.
633 EXPECT_TRUE(NavigateToURL(shell(), isolated_url));
634 EXPECT_NE(web_contents()->GetSiteInstance(), unisolated_instance);
635 EXPECT_NE(web_contents()->GetMainFrame()->GetProcess(), unisolated_process);
636
637 // Go back to www.foo.com: this should end up in the unisolated process.
638 {
639 TestNavigationObserver back_observer(web_contents());
640 web_contents()->GetController().GoBack();
641 back_observer.Wait();
642 }
643
644 EXPECT_EQ(unisolated_instance, web_contents()->GetSiteInstance());
645 EXPECT_EQ(unisolated_process, web_contents()->GetMainFrame()->GetProcess());
646
647 // Go back again. This should go to isolated.foo.com in an isolated process.
648 {
649 TestNavigationObserver back_observer(web_contents());
650 web_contents()->GetController().GoBack();
651 back_observer.Wait();
652 }
653
654 EXPECT_EQ(isolated_instance, web_contents()->GetSiteInstance());
655 EXPECT_NE(unisolated_process, web_contents()->GetMainFrame()->GetProcess());
656
657 // Do a renderer-initiated navigation from isolated.foo.com to another
658 // isolated origin and ensure there is a different isolated process.
659 GURL second_isolated_url(
660 embedded_test_server()->GetURL("isolated.bar.com", "/title3.html"));
661 EXPECT_TRUE(NavigateToURLFromRenderer(web_contents(), second_isolated_url));
662 EXPECT_EQ(GURL("http://isolated.bar.com/"),
663 web_contents()->GetSiteInstance()->GetSiteURL());
664 EXPECT_NE(isolated_instance, web_contents()->GetSiteInstance());
665 EXPECT_NE(unisolated_instance, web_contents()->GetSiteInstance());
666 }
667
668 // Check that opening a popup for an isolated origin puts it into a new process
669 // and its own SiteInstance.
IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,Popup)670 IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, Popup) {
671 GURL unisolated_url(
672 embedded_test_server()->GetURL("foo.com", "/title1.html"));
673 GURL isolated_url(
674 embedded_test_server()->GetURL("isolated.foo.com", "/title2.html"));
675
676 EXPECT_TRUE(NavigateToURL(shell(), unisolated_url));
677
678 // Open a popup to a URL with an isolated origin and ensure that there was a
679 // process swap.
680 Shell* popup = OpenPopup(shell(), isolated_url, "foo");
681
682 EXPECT_NE(shell()->web_contents()->GetSiteInstance(),
683 popup->web_contents()->GetSiteInstance());
684
685 // The popup's site URL should match the full isolated origin.
686 EXPECT_EQ(GURL("http://isolated.foo.com/"),
687 popup->web_contents()->GetSiteInstance()->GetSiteURL());
688
689 // Now open a second popup from an isolated origin to a URL with an
690 // unisolated origin and ensure that there was another process swap.
691 Shell* popup2 = OpenPopup(popup, unisolated_url, "bar");
692 EXPECT_EQ(shell()->web_contents()->GetSiteInstance(),
693 popup2->web_contents()->GetSiteInstance());
694 EXPECT_NE(popup->web_contents()->GetSiteInstance(),
695 popup2->web_contents()->GetSiteInstance());
696 }
697
698 // Check that navigating a subframe to an isolated origin puts the subframe
699 // into an OOPIF and its own SiteInstance. Also check that the isolated
700 // frame's subframes also end up in correct SiteInstance.
IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,Subframe)701 IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, Subframe) {
702 GURL top_url(
703 embedded_test_server()->GetURL("www.foo.com", "/page_with_iframe.html"));
704 EXPECT_TRUE(NavigateToURL(shell(), top_url));
705
706 GURL isolated_url(embedded_test_server()->GetURL("isolated.foo.com",
707 "/page_with_iframe.html"));
708
709 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
710 FrameTreeNode* child = root->child_at(0);
711
712 NavigateIframeToURL(web_contents(), "test_iframe", isolated_url);
713 EXPECT_EQ(child->current_url(), isolated_url);
714
715 // Verify that the child frame is an OOPIF with a different SiteInstance.
716 EXPECT_NE(web_contents()->GetSiteInstance(),
717 child->current_frame_host()->GetSiteInstance());
718 EXPECT_TRUE(child->current_frame_host()->IsCrossProcessSubframe());
719 EXPECT_EQ(GURL("http://isolated.foo.com/"),
720 child->current_frame_host()->GetSiteInstance()->GetSiteURL());
721
722 // Verify that the isolated frame's subframe (which starts out at a relative
723 // path) is kept in the isolated parent's SiteInstance.
724 FrameTreeNode* grandchild = child->child_at(0);
725 EXPECT_EQ(child->current_frame_host()->GetSiteInstance(),
726 grandchild->current_frame_host()->GetSiteInstance());
727
728 // Navigating the grandchild to www.foo.com should put it into the top
729 // frame's SiteInstance.
730 GURL non_isolated_url(
731 embedded_test_server()->GetURL("www.foo.com", "/title3.html"));
732 TestFrameNavigationObserver observer(grandchild);
733 EXPECT_TRUE(ExecuteScript(
734 grandchild, "location.href = '" + non_isolated_url.spec() + "';"));
735 observer.Wait();
736 EXPECT_EQ(non_isolated_url, grandchild->current_url());
737
738 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
739 grandchild->current_frame_host()->GetSiteInstance());
740 EXPECT_NE(child->current_frame_host()->GetSiteInstance(),
741 grandchild->current_frame_host()->GetSiteInstance());
742 }
743
744 // Check that when an non-isolated origin foo.com embeds a subframe from an
745 // isolated origin, which then navigates to a non-isolated origin bar.com,
746 // bar.com goes back to the main frame's SiteInstance. See
747 // https://crbug.com/711006.
IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,NoOOPIFWhenIsolatedOriginNavigatesToNonIsolatedOrigin)748 IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,
749 NoOOPIFWhenIsolatedOriginNavigatesToNonIsolatedOrigin) {
750 if (AreAllSitesIsolatedForTesting())
751 return;
752
753 GURL top_url(
754 embedded_test_server()->GetURL("www.foo.com", "/page_with_iframe.html"));
755 EXPECT_TRUE(NavigateToURL(shell(), top_url));
756
757 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
758 FrameTreeNode* child = root->child_at(0);
759
760 GURL isolated_url(embedded_test_server()->GetURL("isolated.foo.com",
761 "/page_with_iframe.html"));
762
763 NavigateIframeToURL(web_contents(), "test_iframe", isolated_url);
764 EXPECT_EQ(isolated_url, child->current_url());
765
766 // Verify that the child frame is an OOPIF with a different SiteInstance.
767 EXPECT_NE(web_contents()->GetSiteInstance(),
768 child->current_frame_host()->GetSiteInstance());
769 EXPECT_TRUE(child->current_frame_host()->IsCrossProcessSubframe());
770 EXPECT_EQ(GURL("http://isolated.foo.com/"),
771 child->current_frame_host()->GetSiteInstance()->GetSiteURL());
772
773 // Navigate the child frame cross-site, but to a non-isolated origin. When
774 // not in --site-per-process, this should bring the subframe back into the
775 // main frame's SiteInstance.
776 GURL bar_url(embedded_test_server()->GetURL("bar.com", "/title1.html"));
777 EXPECT_FALSE(IsIsolatedOrigin(bar_url));
778 NavigateIframeToURL(web_contents(), "test_iframe", bar_url);
779 EXPECT_EQ(web_contents()->GetSiteInstance(),
780 child->current_frame_host()->GetSiteInstance());
781 EXPECT_FALSE(child->current_frame_host()->IsCrossProcessSubframe());
782 }
783
784 // Check that a new isolated origin subframe will attempt to reuse an existing
785 // process for that isolated origin, even across BrowsingInstances. Also check
786 // that main frame navigations to an isolated origin keep using the default
787 // process model and do not reuse existing processes.
IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,SubframeReusesExistingProcess)788 IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, SubframeReusesExistingProcess) {
789 GURL top_url(
790 embedded_test_server()->GetURL("www.foo.com", "/page_with_iframe.html"));
791 EXPECT_TRUE(NavigateToURL(shell(), top_url));
792 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
793 FrameTreeNode* child = root->child_at(0);
794
795 // Open an unrelated tab in a separate BrowsingInstance, and navigate it to
796 // to an isolated origin. This SiteInstance should have a default process
797 // reuse policy - only subframes attempt process reuse.
798 GURL isolated_url(embedded_test_server()->GetURL("isolated.foo.com",
799 "/page_with_iframe.html"));
800 Shell* second_shell = CreateBrowser();
801 EXPECT_TRUE(NavigateToURL(second_shell, isolated_url));
802 scoped_refptr<SiteInstanceImpl> second_shell_instance =
803 static_cast<SiteInstanceImpl*>(
804 second_shell->web_contents()->GetMainFrame()->GetSiteInstance());
805 EXPECT_FALSE(second_shell_instance->IsRelatedSiteInstance(
806 root->current_frame_host()->GetSiteInstance()));
807 RenderProcessHost* isolated_process = second_shell_instance->GetProcess();
808 EXPECT_EQ(SiteInstanceImpl::ProcessReusePolicy::DEFAULT,
809 second_shell_instance->process_reuse_policy());
810
811 // Now navigate the first tab's subframe to an isolated origin. See that it
812 // reuses the existing |isolated_process|.
813 NavigateIframeToURL(web_contents(), "test_iframe", isolated_url);
814 EXPECT_EQ(isolated_url, child->current_url());
815 EXPECT_EQ(isolated_process, child->current_frame_host()->GetProcess());
816 EXPECT_EQ(
817 SiteInstanceImpl::ProcessReusePolicy::REUSE_PENDING_OR_COMMITTED_SITE,
818 child->current_frame_host()->GetSiteInstance()->process_reuse_policy());
819
820 EXPECT_TRUE(child->current_frame_host()->IsCrossProcessSubframe());
821 EXPECT_EQ(GURL("http://isolated.foo.com/"),
822 child->current_frame_host()->GetSiteInstance()->GetSiteURL());
823
824 // The subframe's SiteInstance should still be different from second_shell's
825 // SiteInstance, and they should be in separate BrowsingInstances.
826 EXPECT_NE(second_shell_instance,
827 child->current_frame_host()->GetSiteInstance());
828 EXPECT_FALSE(second_shell_instance->IsRelatedSiteInstance(
829 child->current_frame_host()->GetSiteInstance()));
830
831 // Navigate the second tab to a normal URL with a same-site subframe. This
832 // leaves only the first tab's subframe in the isolated origin process.
833 EXPECT_TRUE(NavigateToURL(second_shell, top_url));
834 EXPECT_NE(isolated_process,
835 second_shell->web_contents()->GetMainFrame()->GetProcess());
836
837 // Navigate the second tab's subframe to an isolated origin, and check that
838 // this new subframe reuses the isolated process of the subframe in the first
839 // tab, even though the two are in separate BrowsingInstances.
840 NavigateIframeToURL(second_shell->web_contents(), "test_iframe",
841 isolated_url);
842 FrameTreeNode* second_subframe =
843 static_cast<WebContentsImpl*>(second_shell->web_contents())
844 ->GetFrameTree()
845 ->root()
846 ->child_at(0);
847 EXPECT_EQ(isolated_process,
848 second_subframe->current_frame_host()->GetProcess());
849 EXPECT_NE(child->current_frame_host()->GetSiteInstance(),
850 second_subframe->current_frame_host()->GetSiteInstance());
851
852 // Open a third, unrelated tab, navigate it to an isolated origin, and check
853 // that its main frame doesn't share a process with the existing isolated
854 // subframes.
855 Shell* third_shell = CreateBrowser();
856 EXPECT_TRUE(NavigateToURL(third_shell, isolated_url));
857 SiteInstanceImpl* third_shell_instance = static_cast<SiteInstanceImpl*>(
858 third_shell->web_contents()->GetMainFrame()->GetSiteInstance());
859 EXPECT_NE(third_shell_instance,
860 second_subframe->current_frame_host()->GetSiteInstance());
861 EXPECT_NE(third_shell_instance,
862 child->current_frame_host()->GetSiteInstance());
863 EXPECT_NE(third_shell_instance->GetProcess(), isolated_process);
864 }
865
866 // Check that when a cross-site, non-isolated-origin iframe opens a popup,
867 // navigates it to an isolated origin, and then the popup navigates back to its
868 // opener iframe's site, the popup and the opener iframe end up in the same
869 // process and can script each other. See https://crbug.com/796912.
IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,PopupNavigatesToIsolatedOriginAndBack)870 IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,
871 PopupNavigatesToIsolatedOriginAndBack) {
872 // Start on a page with same-site iframe.
873 GURL foo_url(
874 embedded_test_server()->GetURL("www.foo.com", "/page_with_iframe.html"));
875 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
876 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
877 FrameTreeNode* child = root->child_at(0);
878
879 // Navigate iframe cross-site, but not to an isolated origin. This should
880 // stay in the main frame's SiteInstance, unless we're in --site-per-process
881 // mode. (Note that the bug for which this test is written is exclusive to
882 // --isolate-origins and does not happen with --site-per-process.)
883 GURL bar_url(embedded_test_server()->GetURL("bar.com", "/title1.html"));
884 NavigateIframeToURL(web_contents(), "test_iframe", bar_url);
885 if (AreAllSitesIsolatedForTesting()) {
886 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
887 child->current_frame_host()->GetSiteInstance());
888 } else {
889 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
890 child->current_frame_host()->GetSiteInstance());
891 }
892
893 // Open a blank popup from the iframe.
894 ShellAddedObserver new_shell_observer;
895 EXPECT_TRUE(ExecuteScript(child, "window.w = window.open();"));
896 Shell* new_shell = new_shell_observer.GetShell();
897
898 // Have the opener iframe navigate the popup to an isolated origin.
899 GURL isolated_url(
900 embedded_test_server()->GetURL("isolated.foo.com", "/title1.html"));
901 {
902 TestNavigationManager manager(new_shell->web_contents(), isolated_url);
903 EXPECT_TRUE(ExecuteScript(
904 child, "window.w.location.href = '" + isolated_url.spec() + "';"));
905 manager.WaitForNavigationFinished();
906 }
907
908 // Simulate the isolated origin in the popup navigating back to bar.com.
909 GURL bar_url2(embedded_test_server()->GetURL("bar.com", "/title2.html"));
910 {
911 TestNavigationManager manager(new_shell->web_contents(), bar_url2);
912 EXPECT_TRUE(
913 ExecuteScript(new_shell, "location.href = '" + bar_url2.spec() + "';"));
914 manager.WaitForNavigationFinished();
915 }
916
917 // Check that the popup ended up in the same SiteInstance as its same-site
918 // opener iframe.
919 EXPECT_EQ(new_shell->web_contents()->GetMainFrame()->GetSiteInstance(),
920 child->current_frame_host()->GetSiteInstance());
921
922 // Check that the opener iframe can script the popup.
923 std::string popup_location;
924 EXPECT_TRUE(ExecuteScriptAndExtractString(
925 child, "domAutomationController.send(window.w.location.href);",
926 &popup_location));
927 EXPECT_EQ(bar_url2.spec(), popup_location);
928 }
929
930 // Check that when a non-isolated-origin page opens a popup, navigates it
931 // to an isolated origin, and then the popup navigates to a third non-isolated
932 // origin and finally back to its opener's origin, the popup and the opener
933 // iframe end up in the same process and can script each other:
934 //
935 // foo.com
936 // |
937 // window.open()
938 // |
939 // V
940 // about:blank -> isolated.foo.com -> bar.com -> foo.com
941 //
942 // This is a variant of PopupNavigatesToIsolatedOriginAndBack where the popup
943 // navigates to a third site before coming back to the opener's site. See
944 // https://crbug.com/807184.
IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,PopupNavigatesToIsolatedOriginThenToAnotherSiteAndBack)945 IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,
946 PopupNavigatesToIsolatedOriginThenToAnotherSiteAndBack) {
947 // Start on www.foo.com.
948 GURL foo_url(embedded_test_server()->GetURL("www.foo.com", "/title1.html"));
949 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
950 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
951
952 // Open a blank popup.
953 ShellAddedObserver new_shell_observer;
954 EXPECT_TRUE(ExecuteScript(root, "window.w = window.open();"));
955 Shell* new_shell = new_shell_observer.GetShell();
956
957 // Have the opener navigate the popup to an isolated origin.
958 GURL isolated_url(
959 embedded_test_server()->GetURL("isolated.foo.com", "/title1.html"));
960 {
961 TestNavigationManager manager(new_shell->web_contents(), isolated_url);
962 EXPECT_TRUE(ExecuteScript(
963 root, "window.w.location.href = '" + isolated_url.spec() + "';"));
964 manager.WaitForNavigationFinished();
965 }
966
967 // Simulate the isolated origin in the popup navigating to bar.com.
968 GURL bar_url(embedded_test_server()->GetURL("bar.com", "/title2.html"));
969 {
970 TestNavigationManager manager(new_shell->web_contents(), bar_url);
971 EXPECT_TRUE(
972 ExecuteScript(new_shell, "location.href = '" + bar_url.spec() + "';"));
973 manager.WaitForNavigationFinished();
974 }
975
976 const SiteInstanceImpl* const root_site_instance_impl =
977 static_cast<SiteInstanceImpl*>(
978 root->current_frame_host()->GetSiteInstance());
979 const SiteInstanceImpl* const newshell_site_instance_impl =
980 static_cast<SiteInstanceImpl*>(
981 new_shell->web_contents()->GetMainFrame()->GetSiteInstance());
982 if (AreDefaultSiteInstancesEnabled()) {
983 // When default SiteInstances are enabled, all sites that do not
984 // require a dedicated process all end up in the same default SiteInstance.
985 EXPECT_EQ(newshell_site_instance_impl, root_site_instance_impl);
986 EXPECT_TRUE(newshell_site_instance_impl->IsDefaultSiteInstance());
987 } else {
988 // At this point, the popup and the opener should still be in separate
989 // SiteInstances.
990 EXPECT_NE(newshell_site_instance_impl, root_site_instance_impl);
991 EXPECT_NE(AreAllSitesIsolatedForTesting(),
992 newshell_site_instance_impl->IsDefaultSiteInstance());
993 EXPECT_FALSE(root_site_instance_impl->IsDefaultSiteInstance());
994 }
995
996 // Simulate the isolated origin in the popup navigating to www.foo.com.
997 {
998 TestNavigationManager manager(new_shell->web_contents(), foo_url);
999 EXPECT_TRUE(
1000 ExecuteScript(new_shell, "location.href = '" + foo_url.spec() + "';"));
1001 manager.WaitForNavigationFinished();
1002 }
1003
1004 // The popup should now be in the same SiteInstance as its same-site opener.
1005 EXPECT_EQ(new_shell->web_contents()->GetMainFrame()->GetSiteInstance(),
1006 root->current_frame_host()->GetSiteInstance());
1007
1008 // Check that the popup can script the opener.
1009 std::string opener_location;
1010 EXPECT_TRUE(ExecuteScriptAndExtractString(
1011 new_shell, "domAutomationController.send(window.opener.location.href);",
1012 &opener_location));
1013 EXPECT_EQ(foo_url.spec(), opener_location);
1014 }
1015
1016 // Check that with an ABA hierarchy, where B is an isolated origin, the root
1017 // and grandchild frames end up in the same process and can script each other.
1018 // See https://crbug.com/796912.
IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,IsolatedOriginSubframeCreatesGrandchildInRootSite)1019 IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,
1020 IsolatedOriginSubframeCreatesGrandchildInRootSite) {
1021 // Start at foo.com and do a cross-site, renderer-initiated navigation to
1022 // bar.com, which should stay in the same SiteInstance (outside of
1023 // --site-per-process mode). This sets up the main frame such that its
1024 // SiteInstance's site URL does not match its actual origin - a prerequisite
1025 // for https://crbug.com/796912 to happen.
1026 GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
1027 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
1028 GURL bar_url(
1029 embedded_test_server()->GetURL("bar.com", "/page_with_iframe.html"));
1030 TestNavigationObserver observer(web_contents());
1031 EXPECT_TRUE(
1032 ExecuteScript(shell(), "location.href = '" + bar_url.spec() + "';"));
1033 observer.Wait();
1034
1035 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
1036 FrameTreeNode* child = root->child_at(0);
1037
1038 // Navigate bar.com's subframe to an isolated origin with its own subframe.
1039 GURL isolated_url(embedded_test_server()->GetURL("isolated.foo.com",
1040 "/page_with_iframe.html"));
1041 NavigateIframeToURL(web_contents(), "test_iframe", isolated_url);
1042 EXPECT_EQ(isolated_url, child->current_url());
1043 FrameTreeNode* grandchild = child->child_at(0);
1044
1045 // Navigate the isolated origin's subframe back to bar.com, completing the
1046 // ABA hierarchy.
1047 NavigateFrameToURL(grandchild, bar_url);
1048
1049 // The root and grandchild should be in the same SiteInstance, and the
1050 // middle child should be in a different SiteInstance.
1051 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
1052 child->current_frame_host()->GetSiteInstance());
1053 EXPECT_NE(child->current_frame_host()->GetSiteInstance(),
1054 grandchild->current_frame_host()->GetSiteInstance());
1055 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
1056 grandchild->current_frame_host()->GetSiteInstance());
1057
1058 // Check that the root frame can script the same-site grandchild frame.
1059 std::string location;
1060 EXPECT_TRUE(ExecuteScriptAndExtractString(
1061 root, "domAutomationController.send(frames[0][0].location.href);",
1062 &location));
1063 EXPECT_EQ(bar_url.spec(), location);
1064 }
1065
1066 // Check that isolated origins can access cookies. This requires cookie checks
1067 // on the IO thread to be aware of isolated origins.
IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,Cookies)1068 IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, Cookies) {
1069 GURL isolated_url(
1070 embedded_test_server()->GetURL("isolated.foo.com", "/title2.html"));
1071 EXPECT_TRUE(NavigateToURL(shell(), isolated_url));
1072
1073 EXPECT_TRUE(ExecuteScript(web_contents(), "document.cookie = 'foo=bar';"));
1074
1075 std::string cookie;
1076 EXPECT_TRUE(ExecuteScriptAndExtractString(
1077 web_contents(), "window.domAutomationController.send(document.cookie);",
1078 &cookie));
1079 EXPECT_EQ("foo=bar", cookie);
1080 }
1081
1082 // Check that isolated origins won't be placed into processes for other sites
1083 // when over the process limit.
IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,ProcessLimit)1084 IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, ProcessLimit) {
1085 // Set the process limit to 1.
1086 RenderProcessHost::SetMaxRendererProcessCount(1);
1087
1088 // Navigate to an unisolated foo.com URL with an iframe.
1089 GURL foo_url(
1090 embedded_test_server()->GetURL("www.foo.com", "/page_with_iframe.html"));
1091 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
1092 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
1093 RenderProcessHost* foo_process = root->current_frame_host()->GetProcess();
1094 FrameTreeNode* child = root->child_at(0);
1095
1096 // Navigate iframe to an isolated origin.
1097 GURL isolated_foo_url(
1098 embedded_test_server()->GetURL("isolated.foo.com", "/title2.html"));
1099 NavigateIframeToURL(web_contents(), "test_iframe", isolated_foo_url);
1100
1101 // Ensure that the subframe was rendered in a new process.
1102 EXPECT_NE(child->current_frame_host()->GetProcess(), foo_process);
1103
1104 // Sanity-check IsSuitableHost values for the current processes.
1105 BrowserContext* browser_context = web_contents()->GetBrowserContext();
1106 const IsolationContext& isolation_context =
1107 root->current_frame_host()->GetSiteInstance()->GetIsolationContext();
1108 auto is_suitable_host = [browser_context, &isolation_context](
1109 RenderProcessHost* process, const GURL& url) {
1110 GURL site_url(SiteInstance::GetSiteForURL(browser_context, url));
1111 GURL lock_url(
1112 SiteInstanceImpl::DetermineProcessLockURL(isolation_context, url));
1113 return RenderProcessHostImpl::IsSuitableHost(
1114 process, isolation_context, site_url, lock_url, /* is_guest= */ false);
1115 };
1116 EXPECT_TRUE(is_suitable_host(foo_process, foo_url));
1117 EXPECT_FALSE(is_suitable_host(foo_process, isolated_foo_url));
1118 EXPECT_TRUE(is_suitable_host(child->current_frame_host()->GetProcess(),
1119 isolated_foo_url));
1120 EXPECT_FALSE(
1121 is_suitable_host(child->current_frame_host()->GetProcess(), foo_url));
1122
1123 // Open a new, unrelated tab and navigate it to isolated.foo.com. This
1124 // should use a new, unrelated SiteInstance that reuses the existing isolated
1125 // origin process from first tab's subframe.
1126 Shell* new_shell = CreateBrowser();
1127 EXPECT_TRUE(NavigateToURL(new_shell, isolated_foo_url));
1128 scoped_refptr<SiteInstance> isolated_foo_instance(
1129 new_shell->web_contents()->GetMainFrame()->GetSiteInstance());
1130 RenderProcessHost* isolated_foo_process = isolated_foo_instance->GetProcess();
1131 EXPECT_NE(child->current_frame_host()->GetSiteInstance(),
1132 isolated_foo_instance);
1133 EXPECT_FALSE(isolated_foo_instance->IsRelatedSiteInstance(
1134 child->current_frame_host()->GetSiteInstance()));
1135 // TODO(alexmos): with --site-per-process, this won't currently reuse the
1136 // subframe process, because the new SiteInstance will initialize its
1137 // process while it still has no site (during CreateBrowser()), and since
1138 // dedicated processes can't currently be reused for a SiteInstance with no
1139 // site, this creates a new process. The subsequent navigation to
1140 // |isolated_foo_url| stays in that new process without consulting whether it
1141 // can now reuse a different process. This should be fixed; see
1142 // https://crbug.com/513036. Without --site-per-process, this works because
1143 // the site-less SiteInstance is allowed to reuse the first tab's foo.com
1144 // process (which isn't dedicated), and then it swaps to the isolated.foo.com
1145 // process during navigation.
1146 if (!AreAllSitesIsolatedForTesting())
1147 EXPECT_EQ(child->current_frame_host()->GetProcess(), isolated_foo_process);
1148
1149 // Navigate iframe on the first tab to a non-isolated site. This should swap
1150 // processes so that it does not reuse the isolated origin's process.
1151 RenderFrameDeletedObserver deleted_observer(child->current_frame_host());
1152 NavigateIframeToURL(
1153 web_contents(), "test_iframe",
1154 embedded_test_server()->GetURL("www.foo.com", "/title1.html"));
1155 EXPECT_EQ(foo_process, child->current_frame_host()->GetProcess());
1156 EXPECT_NE(isolated_foo_process, child->current_frame_host()->GetProcess());
1157 deleted_observer.WaitUntilDeleted();
1158
1159 // Navigate iframe back to isolated origin. See that it reuses the
1160 // |new_shell| process.
1161 NavigateIframeToURL(web_contents(), "test_iframe", isolated_foo_url);
1162 EXPECT_NE(foo_process, child->current_frame_host()->GetProcess());
1163 EXPECT_EQ(isolated_foo_process, child->current_frame_host()->GetProcess());
1164
1165 // Navigate iframe to a different isolated origin. Ensure that this creates
1166 // a third process.
1167 GURL isolated_bar_url(
1168 embedded_test_server()->GetURL("isolated.bar.com", "/title3.html"));
1169 NavigateIframeToURL(web_contents(), "test_iframe", isolated_bar_url);
1170 RenderProcessHost* isolated_bar_process =
1171 child->current_frame_host()->GetProcess();
1172 EXPECT_NE(foo_process, isolated_bar_process);
1173 EXPECT_NE(isolated_foo_process, isolated_bar_process);
1174
1175 // The new process should only be suitable to host isolated.bar.com, not
1176 // regular web URLs or other isolated origins.
1177 EXPECT_TRUE(is_suitable_host(isolated_bar_process, isolated_bar_url));
1178 EXPECT_FALSE(is_suitable_host(isolated_bar_process, foo_url));
1179 EXPECT_FALSE(is_suitable_host(isolated_bar_process, isolated_foo_url));
1180
1181 // Navigate second tab (currently at isolated.foo.com) to the
1182 // second isolated origin, and see that it switches processes.
1183 EXPECT_TRUE(NavigateToURL(new_shell, isolated_bar_url));
1184 EXPECT_NE(foo_process,
1185 new_shell->web_contents()->GetMainFrame()->GetProcess());
1186 EXPECT_NE(isolated_foo_process,
1187 new_shell->web_contents()->GetMainFrame()->GetProcess());
1188 EXPECT_EQ(isolated_bar_process,
1189 new_shell->web_contents()->GetMainFrame()->GetProcess());
1190
1191 // Navigate second tab to a non-isolated URL and see that it goes back into
1192 // the www.foo.com process, and that it does not share processes with any
1193 // isolated origins.
1194 EXPECT_TRUE(NavigateToURL(new_shell, foo_url));
1195 EXPECT_EQ(foo_process,
1196 new_shell->web_contents()->GetMainFrame()->GetProcess());
1197 EXPECT_NE(isolated_foo_process,
1198 new_shell->web_contents()->GetMainFrame()->GetProcess());
1199 EXPECT_NE(isolated_bar_process,
1200 new_shell->web_contents()->GetMainFrame()->GetProcess());
1201 }
1202
1203 // Verify that a navigation to an non-isolated origin does not reuse a process
1204 // from a pending navigation to an isolated origin. See
1205 // https://crbug.com/738634.
IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,ProcessReuseWithResponseStartedFromIsolatedOrigin)1206 IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,
1207 ProcessReuseWithResponseStartedFromIsolatedOrigin) {
1208 // Set the process limit to 1.
1209 RenderProcessHost::SetMaxRendererProcessCount(1);
1210
1211 // Start, but don't commit a navigation to an unisolated foo.com URL.
1212 GURL slow_url(embedded_test_server()->GetURL("www.foo.com", "/title1.html"));
1213 NavigationController::LoadURLParams load_params(slow_url);
1214 TestNavigationManager foo_delayer(shell()->web_contents(), slow_url);
1215 shell()->web_contents()->GetController().LoadURL(
1216 slow_url, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
1217 EXPECT_TRUE(foo_delayer.WaitForRequestStart());
1218
1219 // Open a new, unrelated tab and navigate it to isolated.foo.com.
1220 Shell* new_shell = CreateBrowser();
1221 GURL isolated_url(
1222 embedded_test_server()->GetURL("isolated.foo.com", "/title2.html"));
1223 TestNavigationManager isolated_delayer(new_shell->web_contents(),
1224 isolated_url);
1225 new_shell->web_contents()->GetController().LoadURL(
1226 isolated_url, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
1227
1228 // Wait for the response from the isolated origin. After this returns, we made
1229 // the final pick for the process to use for this navigation as part of
1230 // NavigationRequest::OnResponseStarted.
1231 EXPECT_TRUE(isolated_delayer.WaitForResponse());
1232
1233 // Now, proceed with the response and commit the non-isolated URL. This
1234 // should notice that the process that was picked for this navigation is not
1235 // suitable anymore, as it should have been locked to isolated.foo.com.
1236 foo_delayer.WaitForNavigationFinished();
1237
1238 // Commit the isolated origin.
1239 isolated_delayer.WaitForNavigationFinished();
1240
1241 // Ensure that the isolated origin did not share a process with the first
1242 // tab.
1243 EXPECT_NE(web_contents()->GetMainFrame()->GetProcess(),
1244 new_shell->web_contents()->GetMainFrame()->GetProcess());
1245 }
1246
1247 // When a navigation uses a siteless SiteInstance, and a second navigation
1248 // commits an isolated origin which reuses the siteless SiteInstance's process
1249 // before the first navigation's response is received, ensure that the first
1250 // navigation can still finish properly and transfer to a new process, without
1251 // an origin lock mismatch. See https://crbug.com/773809.
IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,ProcessReuseWithLazilyAssignedSiteInstance)1252 IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,
1253 ProcessReuseWithLazilyAssignedSiteInstance) {
1254 // Set the process limit to 1.
1255 RenderProcessHost::SetMaxRendererProcessCount(1);
1256
1257 // Start from an about:blank page, where the SiteInstance will not have a
1258 // site assigned, but will have an associated process.
1259 EXPECT_TRUE(NavigateToURL(shell(), GURL(url::kAboutBlankURL)));
1260 SiteInstanceImpl* starting_site_instance = static_cast<SiteInstanceImpl*>(
1261 shell()->web_contents()->GetMainFrame()->GetSiteInstance());
1262 EXPECT_FALSE(starting_site_instance->HasSite());
1263 EXPECT_TRUE(starting_site_instance->HasProcess());
1264
1265 // Inject and click a link to a non-isolated origin www.foo.com. Note that
1266 // setting location.href won't work here, as that goes through OpenURL
1267 // instead of OnBeginNavigation when starting from an about:blank page, and
1268 // that doesn't trigger this bug.
1269 GURL foo_url(embedded_test_server()->GetURL("www.foo.com", "/title1.html"));
1270 TestNavigationManager manager(shell()->web_contents(), foo_url);
1271 InjectAndClickLinkTo(foo_url);
1272 EXPECT_TRUE(manager.WaitForRequestStart());
1273
1274 // Before response is received, open a new, unrelated tab and navigate it to
1275 // isolated.foo.com. This reuses the first process, which is still considered
1276 // unused at this point, and locks it to isolated.foo.com.
1277 Shell* new_shell = CreateBrowser();
1278 GURL isolated_url(
1279 embedded_test_server()->GetURL("isolated.foo.com", "/title2.html"));
1280 EXPECT_TRUE(NavigateToURL(new_shell, isolated_url));
1281 EXPECT_EQ(web_contents()->GetMainFrame()->GetProcess(),
1282 new_shell->web_contents()->GetMainFrame()->GetProcess());
1283
1284 // Wait for response from the first tab. This should notice that the first
1285 // process is no longer suitable for the final destination (which is an
1286 // unisolated URL) and transfer to another process. In
1287 // https://crbug.com/773809, this led to a CHECK due to origin lock mismatch.
1288 manager.WaitForNavigationFinished();
1289
1290 // Ensure that the isolated origin did not share a process with the first
1291 // tab.
1292 EXPECT_NE(web_contents()->GetMainFrame()->GetProcess(),
1293 new_shell->web_contents()->GetMainFrame()->GetProcess());
1294 }
1295
1296 // Same as ProcessReuseWithLazilyAssignedSiteInstance above, but here the
1297 // navigation with a siteless SiteInstance is for an isolated origin, and the
1298 // unrelated tab loads an unisolated URL which reuses the siteless
1299 // SiteInstance's process. Although the unisolated URL won't lock that process
1300 // to an origin (except when running with --site-per-process), it should still
1301 // mark it as used and cause the isolated origin to transfer when it receives a
1302 // response. See https://crbug.com/773809.
IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,ProcessReuseWithLazilyAssignedIsolatedSiteInstance)1303 IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,
1304 ProcessReuseWithLazilyAssignedIsolatedSiteInstance) {
1305 // Set the process limit to 1.
1306 RenderProcessHost::SetMaxRendererProcessCount(1);
1307
1308 // Start from an about:blank page, where the SiteInstance will not have a
1309 // site assigned, but will have an associated process.
1310 EXPECT_TRUE(NavigateToURL(shell(), GURL(url::kAboutBlankURL)));
1311 SiteInstanceImpl* starting_site_instance = static_cast<SiteInstanceImpl*>(
1312 shell()->web_contents()->GetMainFrame()->GetSiteInstance());
1313 EXPECT_FALSE(starting_site_instance->HasSite());
1314 EXPECT_TRUE(starting_site_instance->HasProcess());
1315 EXPECT_TRUE(web_contents()->GetMainFrame()->GetProcess()->IsUnused());
1316
1317 // Inject and click a link to an isolated origin. Note that
1318 // setting location.href won't work here, as that goes through OpenURL
1319 // instead of OnBeginNavigation when starting from an about:blank page, and
1320 // that doesn't trigger this bug.
1321 GURL isolated_url(
1322 embedded_test_server()->GetURL("isolated.foo.com", "/title2.html"));
1323 TestNavigationManager manager(shell()->web_contents(), isolated_url);
1324 InjectAndClickLinkTo(isolated_url);
1325 EXPECT_TRUE(manager.WaitForRequestStart());
1326
1327 // Before response is received, open a new, unrelated tab and navigate it to
1328 // an unisolated URL. This should reuse the first process, which is still
1329 // considered unused at this point, and marks it as used.
1330 Shell* new_shell = CreateBrowser();
1331 GURL foo_url(embedded_test_server()->GetURL("www.foo.com", "/title1.html"));
1332 EXPECT_TRUE(NavigateToURL(new_shell, foo_url));
1333 EXPECT_EQ(web_contents()->GetMainFrame()->GetProcess(),
1334 new_shell->web_contents()->GetMainFrame()->GetProcess());
1335 EXPECT_FALSE(web_contents()->GetMainFrame()->GetProcess()->IsUnused());
1336
1337 // Wait for response in the first tab. This should notice that the first
1338 // process is no longer suitable for the isolated origin because it should
1339 // already be marked as used, and transfer to another process.
1340 manager.WaitForNavigationFinished();
1341
1342 // Ensure that the isolated origin did not share a process with the second
1343 // tab.
1344 EXPECT_NE(web_contents()->GetMainFrame()->GetProcess(),
1345 new_shell->web_contents()->GetMainFrame()->GetProcess());
1346 }
1347
1348 // Verify that a navigation to an unisolated origin cannot reuse a process from
1349 // a pending navigation to an isolated origin. Similar to
1350 // ProcessReuseWithResponseStartedFromIsolatedOrigin, but here the non-isolated
1351 // URL is the first to reach OnResponseStarted, which should mark the process
1352 // as "used", so that the isolated origin can't reuse it. See
1353 // https://crbug.com/738634.
IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,ProcessReuseWithResponseStartedFromUnisolatedOrigin)1354 IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,
1355 ProcessReuseWithResponseStartedFromUnisolatedOrigin) {
1356 // Set the process limit to 1.
1357 RenderProcessHost::SetMaxRendererProcessCount(1);
1358
1359 // Start a navigation to an unisolated foo.com URL.
1360 GURL slow_url(embedded_test_server()->GetURL("www.foo.com", "/title1.html"));
1361 NavigationController::LoadURLParams load_params(slow_url);
1362 TestNavigationManager foo_delayer(shell()->web_contents(), slow_url);
1363 shell()->web_contents()->GetController().LoadURL(
1364 slow_url, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
1365
1366 // Wait for the response for foo.com. After this returns, we should have made
1367 // the final pick for the process to use for foo.com, so this should mark the
1368 // process as "used" and ineligible for reuse by isolated.foo.com below.
1369 EXPECT_TRUE(foo_delayer.WaitForResponse());
1370
1371 // Open a new, unrelated tab, navigate it to isolated.foo.com, and wait for
1372 // the navigation to fully load.
1373 Shell* new_shell = CreateBrowser();
1374 GURL isolated_url(
1375 embedded_test_server()->GetURL("isolated.foo.com", "/title2.html"));
1376 EXPECT_TRUE(NavigateToURL(new_shell, isolated_url));
1377
1378 // Finish loading the foo.com URL.
1379 foo_delayer.WaitForNavigationFinished();
1380
1381 // Ensure that the isolated origin did not share a process with the first
1382 // tab.
1383 EXPECT_NE(web_contents()->GetMainFrame()->GetProcess(),
1384 new_shell->web_contents()->GetMainFrame()->GetProcess());
1385 }
1386
1387 // Verify that when a process has a pending SiteProcessCountTracker entry for
1388 // an isolated origin, and a navigation to a non-isolated origin reuses that
1389 // process, future isolated origin subframe navigations do not reuse that
1390 // process. See https://crbug.com/780661.
IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,IsolatedSubframeDoesNotReuseUnsuitableProcessWithPendingSiteEntry)1391 IN_PROC_BROWSER_TEST_F(
1392 IsolatedOriginTest,
1393 IsolatedSubframeDoesNotReuseUnsuitableProcessWithPendingSiteEntry) {
1394 // Set the process limit to 1.
1395 RenderProcessHost::SetMaxRendererProcessCount(1);
1396
1397 // Start from an about:blank page, where the SiteInstance will not have a
1398 // site assigned, but will have an associated process.
1399 EXPECT_TRUE(NavigateToURL(shell(), GURL(url::kAboutBlankURL)));
1400 EXPECT_TRUE(web_contents()->GetMainFrame()->GetProcess()->IsUnused());
1401
1402 // Inject and click a link to an isolated origin URL which never sends back a
1403 // response.
1404 GURL hung_isolated_url(
1405 embedded_test_server()->GetURL("isolated.foo.com", "/hung"));
1406 TestNavigationManager manager(web_contents(), hung_isolated_url);
1407 InjectAndClickLinkTo(hung_isolated_url);
1408
1409 // Wait for the request and send it. This will place
1410 // isolated.foo.com on the list of pending sites for this tab's process.
1411 EXPECT_TRUE(manager.WaitForRequestStart());
1412 manager.ResumeNavigation();
1413
1414 // Open a new, unrelated tab and navigate it to an unisolated URL. This
1415 // should reuse the first process, which is still considered unused at this
1416 // point, and mark it as used.
1417 Shell* new_shell = CreateBrowser();
1418 GURL foo_url(
1419 embedded_test_server()->GetURL("www.foo.com", "/page_with_iframe.html"));
1420 EXPECT_TRUE(NavigateToURL(new_shell, foo_url));
1421
1422 // Navigate iframe on second tab to isolated.foo.com. This should *not*
1423 // reuse the first process, even though isolated.foo.com is still in its list
1424 // of pending sites (from the hung navigation in the first tab). That
1425 // process is unsuitable because it now contains www.foo.com.
1426 GURL isolated_url(
1427 embedded_test_server()->GetURL("isolated.foo.com", "/title1.html"));
1428 NavigateIframeToURL(new_shell->web_contents(), "test_iframe", isolated_url);
1429
1430 FrameTreeNode* root = static_cast<WebContentsImpl*>(new_shell->web_contents())
1431 ->GetFrameTree()
1432 ->root();
1433 FrameTreeNode* child = root->child_at(0);
1434 EXPECT_NE(child->current_frame_host()->GetProcess(),
1435 root->current_frame_host()->GetProcess());
1436
1437 // Manipulating cookies from the main frame should not result in a renderer
1438 // kill.
1439 EXPECT_TRUE(ExecuteScript(root->current_frame_host(),
1440 "document.cookie = 'foo=bar';"));
1441 std::string cookie;
1442 EXPECT_TRUE(ExecuteScriptAndExtractString(
1443 root->current_frame_host(),
1444 "window.domAutomationController.send(document.cookie);", &cookie));
1445 EXPECT_EQ("foo=bar", cookie);
1446 }
1447
1448 // Similar to the test above, but for a ServiceWorker. When a process has a
1449 // pending SiteProcessCountTracker entry for an isolated origin, and a
1450 // navigation to a non-isolated origin reuses that process, a ServiceWorker
1451 // subsequently created for that isolated origin shouldn't reuse that process.
1452 // See https://crbug.com/780661 and https://crbug.com/780089.
IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,IsolatedServiceWorkerDoesNotReuseUnsuitableProcessWithPendingSiteEntry)1453 IN_PROC_BROWSER_TEST_F(
1454 IsolatedOriginTest,
1455 IsolatedServiceWorkerDoesNotReuseUnsuitableProcessWithPendingSiteEntry) {
1456 // Set the process limit to 1.
1457 RenderProcessHost::SetMaxRendererProcessCount(1);
1458
1459 // Start from an about:blank page, where the SiteInstance will not have a
1460 // site assigned, but will have an associated process.
1461 EXPECT_TRUE(NavigateToURL(shell(), GURL(url::kAboutBlankURL)));
1462 EXPECT_TRUE(web_contents()->GetMainFrame()->GetProcess()->IsUnused());
1463
1464 // Inject and click a link to an isolated origin URL which never sends back a
1465 // response.
1466 GURL hung_isolated_url(
1467 embedded_test_server()->GetURL("isolated.foo.com", "/hung"));
1468 TestNavigationManager manager(shell()->web_contents(), hung_isolated_url);
1469 InjectAndClickLinkTo(hung_isolated_url);
1470
1471 // Wait for the request and send it. This will place
1472 // isolated.foo.com on the list of pending sites for this tab's process.
1473 EXPECT_TRUE(manager.WaitForRequestStart());
1474 manager.ResumeNavigation();
1475
1476 // Open a new, unrelated tab and navigate it to an unisolated URL. This
1477 // should reuse the first process, which is still considered unused at this
1478 // point, and mark it as used.
1479 Shell* new_shell = CreateBrowser();
1480 GURL foo_url(embedded_test_server()->GetURL("www.foo.com", "/title1.html"));
1481 EXPECT_TRUE(NavigateToURL(new_shell, foo_url));
1482
1483 // A SiteInstance created for an isolated origin ServiceWorker should
1484 // not reuse the unsuitable first process.
1485 scoped_refptr<SiteInstanceImpl> sw_site_instance =
1486 SiteInstanceImpl::CreateForServiceWorker(
1487 web_contents()->GetBrowserContext(), hung_isolated_url,
1488 /* can_reuse_process= */ true);
1489 RenderProcessHost* sw_host = sw_site_instance->GetProcess();
1490 EXPECT_NE(new_shell->web_contents()->GetMainFrame()->GetProcess(), sw_host);
1491
1492 // Cancel the hung request and commit a real navigation to an isolated
1493 // origin. This should now end up in the ServiceWorker's process.
1494 web_contents()->GetFrameTree()->root()->ResetNavigationRequest(false);
1495 GURL isolated_url(
1496 embedded_test_server()->GetURL("isolated.foo.com", "/title1.html"));
1497 EXPECT_TRUE(NavigateToURL(shell(), isolated_url));
1498 EXPECT_EQ(web_contents()->GetMainFrame()->GetProcess(), sw_host);
1499 }
1500
1501 // Check that subdomains on an isolated origin (e.g., bar.isolated.foo.com)
1502 // also end up in the isolated origin's SiteInstance.
IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,IsolatedOriginWithSubdomain)1503 IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, IsolatedOriginWithSubdomain) {
1504 // Start on a page with an isolated origin with a same-site iframe.
1505 GURL isolated_url(embedded_test_server()->GetURL("isolated.foo.com",
1506 "/page_with_iframe.html"));
1507 EXPECT_TRUE(NavigateToURL(shell(), isolated_url));
1508
1509 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
1510 FrameTreeNode* child = root->child_at(0);
1511 scoped_refptr<SiteInstance> isolated_instance =
1512 web_contents()->GetSiteInstance();
1513
1514 // Navigate iframe to the isolated origin's subdomain.
1515 GURL isolated_subdomain_url(
1516 embedded_test_server()->GetURL("bar.isolated.foo.com", "/title1.html"));
1517 NavigateIframeToURL(web_contents(), "test_iframe", isolated_subdomain_url);
1518 EXPECT_EQ(child->current_url(), isolated_subdomain_url);
1519
1520 EXPECT_EQ(isolated_instance, child->current_frame_host()->GetSiteInstance());
1521 EXPECT_FALSE(child->current_frame_host()->IsCrossProcessSubframe());
1522 EXPECT_EQ(GURL("http://isolated.foo.com/"),
1523 child->current_frame_host()->GetSiteInstance()->GetSiteURL());
1524
1525 // Now try navigating the main frame (renderer-initiated) to the isolated
1526 // origin's subdomain. This should not swap processes.
1527 TestNavigationObserver observer(web_contents());
1528 EXPECT_TRUE(
1529 ExecuteScript(web_contents(),
1530 "location.href = '" + isolated_subdomain_url.spec() + "'"));
1531 observer.Wait();
1532 EXPECT_EQ(isolated_instance, web_contents()->GetSiteInstance());
1533 }
1534
1535 // This class allows intercepting the OpenLocalStorage method and changing
1536 // the parameters to the real implementation of it.
1537 class StoragePartitonInterceptor
1538 : public blink::mojom::DomStorageInterceptorForTesting,
1539 public RenderProcessHostObserver {
1540 public:
StoragePartitonInterceptor(RenderProcessHostImpl * rph,mojo::PendingReceiver<blink::mojom::DomStorage> receiver,const url::Origin & origin_to_inject)1541 StoragePartitonInterceptor(
1542 RenderProcessHostImpl* rph,
1543 mojo::PendingReceiver<blink::mojom::DomStorage> receiver,
1544 const url::Origin& origin_to_inject)
1545 : origin_to_inject_(origin_to_inject) {
1546 StoragePartitionImpl* storage_partition =
1547 static_cast<StoragePartitionImpl*>(rph->GetStoragePartition());
1548
1549 // Bind the real DomStorage implementation.
1550 mojo::PendingRemote<blink::mojom::DomStorageClient> unused_client;
1551 ignore_result(unused_client.InitWithNewPipeAndPassReceiver());
1552 mojo::ReceiverId receiver_id = storage_partition->BindDomStorage(
1553 rph->GetID(), std::move(receiver), std::move(unused_client));
1554
1555 // Now replace it with this object and keep a pointer to the real
1556 // implementation.
1557 dom_storage_ = storage_partition->dom_storage_receivers_for_testing()
1558 .SwapImplForTesting(receiver_id, this);
1559
1560 // Register the |this| as a RenderProcessHostObserver, so it can be
1561 // correctly cleaned up when the process exits.
1562 rph->AddObserver(this);
1563 }
1564
1565 // Ensure this object is cleaned up when the process goes away, since it
1566 // is not owned by anyone else.
RenderProcessExited(RenderProcessHost * host,const ChildProcessTerminationInfo & info)1567 void RenderProcessExited(RenderProcessHost* host,
1568 const ChildProcessTerminationInfo& info) override {
1569 host->RemoveObserver(this);
1570 delete this;
1571 }
1572
1573 // Allow all methods that aren't explicitly overridden to pass through
1574 // unmodified.
GetForwardingInterface()1575 blink::mojom::DomStorage* GetForwardingInterface() override {
1576 return dom_storage_;
1577 }
1578
1579 // Override this method to allow changing the origin. It simulates a
1580 // renderer process sending incorrect data to the browser process, so
1581 // security checks can be tested.
OpenLocalStorage(const url::Origin & origin,mojo::PendingReceiver<blink::mojom::StorageArea> receiver)1582 void OpenLocalStorage(
1583 const url::Origin& origin,
1584 mojo::PendingReceiver<blink::mojom::StorageArea> receiver) override {
1585 GetForwardingInterface()->OpenLocalStorage(origin_to_inject_,
1586 std::move(receiver));
1587 }
1588
1589 private:
1590 // Keep a pointer to the original implementation of the service, so all
1591 // calls can be forwarded to it.
1592 blink::mojom::DomStorage* dom_storage_;
1593
1594 url::Origin origin_to_inject_;
1595
1596 DISALLOW_COPY_AND_ASSIGN(StoragePartitonInterceptor);
1597 };
1598
CreateTestDomStorageBackend(const url::Origin & origin_to_inject,RenderProcessHostImpl * rph,mojo::PendingReceiver<blink::mojom::DomStorage> receiver)1599 void CreateTestDomStorageBackend(
1600 const url::Origin& origin_to_inject,
1601 RenderProcessHostImpl* rph,
1602 mojo::PendingReceiver<blink::mojom::DomStorage> receiver) {
1603 // This object will register as RenderProcessHostObserver, so it will
1604 // clean itself automatically on process exit.
1605 new StoragePartitonInterceptor(rph, std::move(receiver), origin_to_inject);
1606 }
1607
1608 // Verify that an isolated renderer process cannot read localStorage of an
1609 // origin outside of its isolated site.
IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,LocalStorageOriginEnforcement_IsolatedAccessingNonIsolated)1610 IN_PROC_BROWSER_TEST_F(
1611 IsolatedOriginTest,
1612 LocalStorageOriginEnforcement_IsolatedAccessingNonIsolated) {
1613 auto mismatched_origin = url::Origin::Create(GURL("http://abc.foo.com"));
1614 EXPECT_FALSE(IsIsolatedOrigin(mismatched_origin));
1615 RenderProcessHostImpl::SetDomStorageBinderForTesting(
1616 base::BindRepeating(&CreateTestDomStorageBackend, mismatched_origin));
1617
1618 GURL isolated_url(
1619 embedded_test_server()->GetURL("isolated.foo.com", "/title1.html"));
1620 EXPECT_TRUE(IsIsolatedOrigin(url::Origin::Create(isolated_url)));
1621
1622 EXPECT_TRUE(NavigateToURL(shell(), isolated_url));
1623
1624 content::RenderProcessHostBadIpcMessageWaiter kill_waiter(
1625 shell()->web_contents()->GetMainFrame()->GetProcess());
1626 // Use ignore_result here, since on Android the renderer process is
1627 // terminated, but ExecuteScript still returns true. It properly returns
1628 // false on all other platforms.
1629 ignore_result(ExecuteScript(shell()->web_contents()->GetMainFrame(),
1630 "localStorage.length;"));
1631 EXPECT_EQ(bad_message::RPH_MOJO_PROCESS_ERROR, kill_waiter.Wait());
1632 }
1633
1634 #if defined(OS_ANDROID)
1635 #define MAYBE_LocalStorageOriginEnforcement_NonIsolatedAccessingIsolated \
1636 LocalStorageOriginEnforcement_NonIsolatedAccessingIsolated
1637 #else
1638 // TODO(lukasza): https://crbug.com/566091: Once remote NTP is capable of
1639 // embedding OOPIFs, start enforcing citadel-style checks on desktop
1640 // platforms.
1641 #define MAYBE_LocalStorageOriginEnforcement_NonIsolatedAccessingIsolated \
1642 DISABLED_LocalStorageOriginEnforcement_NonIsolatedAccessingIsolated
1643 #endif
1644 // Verify that a non-isolated renderer process cannot read localStorage of an
1645 // isolated origin.
1646 //
1647 // TODO(alexmos, lukasza): https://crbug.com/764958: Replicate this test for
1648 // the IO-thread case.
IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,MAYBE_LocalStorageOriginEnforcement_NonIsolatedAccessingIsolated)1649 IN_PROC_BROWSER_TEST_F(
1650 IsolatedOriginTest,
1651 MAYBE_LocalStorageOriginEnforcement_NonIsolatedAccessingIsolated) {
1652 auto isolated_origin = url::Origin::Create(GURL("http://isolated.foo.com"));
1653 EXPECT_TRUE(IsIsolatedOrigin(isolated_origin));
1654
1655 GURL nonisolated_url(
1656 embedded_test_server()->GetURL("non-isolated.com", "/title1.html"));
1657 EXPECT_FALSE(IsIsolatedOrigin(url::Origin::Create(nonisolated_url)));
1658
1659 RenderProcessHostImpl::SetDomStorageBinderForTesting(
1660 base::BindRepeating(&CreateTestDomStorageBackend, isolated_origin));
1661 EXPECT_TRUE(NavigateToURL(shell(), nonisolated_url));
1662
1663 content::RenderProcessHostBadIpcMessageWaiter kill_waiter(
1664 shell()->web_contents()->GetMainFrame()->GetProcess());
1665 // Use ignore_result here, since on Android the renderer process is
1666 // terminated, but ExecuteScript still returns true. It properly returns
1667 // false on all other platforms.
1668 ignore_result(ExecuteScript(shell()->web_contents()->GetMainFrame(),
1669 "localStorage.length;"));
1670 EXPECT_EQ(bad_message::RPH_MOJO_PROCESS_ERROR, kill_waiter.Wait());
1671 }
1672
1673 // Verify that an IPC request for reading localStorage of an *opaque* origin
1674 // will be rejected.
IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,LocalStorageOriginEnforcement_OpaqueOrigin)1675 IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,
1676 LocalStorageOriginEnforcement_OpaqueOrigin) {
1677 url::Origin precursor_origin =
1678 url::Origin::Create(GURL("https://non-isolated.com"));
1679 url::Origin opaque_origin = precursor_origin.DeriveNewOpaqueOrigin();
1680 RenderProcessHostImpl::SetDomStorageBinderForTesting(
1681 base::BindRepeating(&CreateTestDomStorageBackend, opaque_origin));
1682
1683 GURL isolated_url(
1684 embedded_test_server()->GetURL("isolated.foo.com", "/title1.html"));
1685 EXPECT_TRUE(IsIsolatedOrigin(url::Origin::Create(isolated_url)));
1686 EXPECT_TRUE(NavigateToURL(shell(), isolated_url));
1687
1688 content::RenderProcessHostBadIpcMessageWaiter kill_waiter(
1689 shell()->web_contents()->GetMainFrame()->GetProcess());
1690 // Use ignore_result here, since on Android the renderer process is
1691 // terminated, but ExecuteScript still returns true. It properly returns
1692 // false on all other platforms.
1693 ignore_result(ExecuteScript(shell()->web_contents()->GetMainFrame(),
1694 "localStorage.length;"));
1695 EXPECT_EQ(bad_message::RPH_MOJO_PROCESS_ERROR, kill_waiter.Wait());
1696 }
1697
1698 class IsolatedOriginFieldTrialTest : public IsolatedOriginTestBase {
1699 public:
IsolatedOriginFieldTrialTest()1700 IsolatedOriginFieldTrialTest() {
1701 scoped_feature_list_.InitAndEnableFeatureWithParameters(
1702 features::kIsolateOrigins,
1703 {{features::kIsolateOriginsFieldTrialParamName,
1704 "https://field.trial.com/,https://bar.com/"}});
1705 }
~IsolatedOriginFieldTrialTest()1706 ~IsolatedOriginFieldTrialTest() override {}
1707
1708 private:
1709 base::test::ScopedFeatureList scoped_feature_list_;
1710
1711 DISALLOW_COPY_AND_ASSIGN(IsolatedOriginFieldTrialTest);
1712 };
1713
IN_PROC_BROWSER_TEST_F(IsolatedOriginFieldTrialTest,Test)1714 IN_PROC_BROWSER_TEST_F(IsolatedOriginFieldTrialTest, Test) {
1715 bool expected_to_isolate = !base::CommandLine::ForCurrentProcess()->HasSwitch(
1716 switches::kDisableSiteIsolation);
1717
1718 EXPECT_EQ(expected_to_isolate,
1719 IsIsolatedOrigin(GURL("https://field.trial.com/")));
1720 EXPECT_EQ(expected_to_isolate, IsIsolatedOrigin(GURL("https://bar.com/")));
1721 }
1722
1723 class IsolatedOriginCommandLineAndFieldTrialTest
1724 : public IsolatedOriginFieldTrialTest {
1725 public:
1726 IsolatedOriginCommandLineAndFieldTrialTest() = default;
1727
SetUpCommandLine(base::CommandLine * command_line)1728 void SetUpCommandLine(base::CommandLine* command_line) override {
1729 command_line->AppendSwitchASCII(
1730 switches::kIsolateOrigins,
1731 "https://cmd.line.com/,https://cmdline.com/");
1732 }
1733
1734 DISALLOW_COPY_AND_ASSIGN(IsolatedOriginCommandLineAndFieldTrialTest);
1735 };
1736
1737 // Verify that the lists of isolated origins specified via --isolate-origins
1738 // and via field trials are merged. See https://crbug.com/894535.
IN_PROC_BROWSER_TEST_F(IsolatedOriginCommandLineAndFieldTrialTest,Test)1739 IN_PROC_BROWSER_TEST_F(IsolatedOriginCommandLineAndFieldTrialTest, Test) {
1740 // --isolate-origins should take effect regardless of the
1741 // kDisableSiteIsolation opt-out flag.
1742 EXPECT_TRUE(IsIsolatedOrigin(GURL("https://cmd.line.com/")));
1743 EXPECT_TRUE(IsIsolatedOrigin(GURL("https://cmdline.com/")));
1744
1745 // Field trial origins should also take effect, but only if the opt-out flag
1746 // is not present.
1747 bool expected_to_isolate = !base::CommandLine::ForCurrentProcess()->HasSwitch(
1748 switches::kDisableSiteIsolation);
1749 EXPECT_EQ(expected_to_isolate,
1750 IsIsolatedOrigin(GURL("https://field.trial.com/")));
1751 EXPECT_EQ(expected_to_isolate, IsIsolatedOrigin(GURL("https://bar.com/")));
1752 }
1753
1754 // This is a regression test for https://crbug.com/793350 - the long list of
1755 // origins to isolate used to be unnecessarily propagated to the renderer
1756 // process, trigerring a crash due to exceeding kZygoteMaxMessageLength.
1757 class IsolatedOriginLongListTest : public IsolatedOriginTestBase {
1758 public:
IsolatedOriginLongListTest()1759 IsolatedOriginLongListTest() {}
~IsolatedOriginLongListTest()1760 ~IsolatedOriginLongListTest() override {}
1761
SetUpCommandLine(base::CommandLine * command_line)1762 void SetUpCommandLine(base::CommandLine* command_line) override {
1763 ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
1764
1765 std::ostringstream origin_list;
1766 origin_list
1767 << embedded_test_server()->GetURL("isolated.foo.com", "/").spec();
1768 for (int i = 0; i < 1000; i++) {
1769 std::ostringstream hostname;
1770 hostname << "foo" << i << ".com";
1771
1772 origin_list << ","
1773 << embedded_test_server()->GetURL(hostname.str(), "/").spec();
1774 }
1775 command_line->AppendSwitchASCII(switches::kIsolateOrigins,
1776 origin_list.str());
1777 }
1778
SetUpOnMainThread()1779 void SetUpOnMainThread() override {
1780 host_resolver()->AddRule("*", "127.0.0.1");
1781 embedded_test_server()->StartAcceptingConnections();
1782 }
1783 };
1784
IN_PROC_BROWSER_TEST_F(IsolatedOriginLongListTest,Test)1785 IN_PROC_BROWSER_TEST_F(IsolatedOriginLongListTest, Test) {
1786 GURL test_url(embedded_test_server()->GetURL(
1787 "bar1.com",
1788 "/cross_site_iframe_factory.html?"
1789 "bar1.com(isolated.foo.com,foo999.com,bar2.com)"));
1790 EXPECT_TRUE(NavigateToURL(shell(), test_url));
1791
1792 EXPECT_EQ(4u, shell()->web_contents()->GetAllFrames().size());
1793 RenderFrameHost* main_frame = shell()->web_contents()->GetMainFrame();
1794 RenderFrameHost* subframe1 = shell()->web_contents()->GetAllFrames()[1];
1795 RenderFrameHost* subframe2 = shell()->web_contents()->GetAllFrames()[2];
1796 RenderFrameHost* subframe3 = shell()->web_contents()->GetAllFrames()[3];
1797 EXPECT_EQ("bar1.com", main_frame->GetLastCommittedOrigin().GetURL().host());
1798 EXPECT_EQ("isolated.foo.com",
1799 subframe1->GetLastCommittedOrigin().GetURL().host());
1800 EXPECT_EQ("foo999.com", subframe2->GetLastCommittedOrigin().GetURL().host());
1801 EXPECT_EQ("bar2.com", subframe3->GetLastCommittedOrigin().GetURL().host());
1802
1803 // bar1.com and bar2.com are not on the list of origins to isolate - they
1804 // should stay in the same process, unless --site-per-process has also been
1805 // specified.
1806 if (!AreAllSitesIsolatedForTesting()) {
1807 EXPECT_EQ(main_frame->GetProcess()->GetID(),
1808 subframe3->GetProcess()->GetID());
1809 EXPECT_EQ(main_frame->GetSiteInstance(), subframe3->GetSiteInstance());
1810 }
1811
1812 // isolated.foo.com and foo999.com are on the list of origins to isolate -
1813 // they should be isolated from everything else.
1814 EXPECT_NE(main_frame->GetProcess()->GetID(),
1815 subframe1->GetProcess()->GetID());
1816 EXPECT_NE(main_frame->GetSiteInstance(), subframe1->GetSiteInstance());
1817 EXPECT_NE(main_frame->GetProcess()->GetID(),
1818 subframe2->GetProcess()->GetID());
1819 EXPECT_NE(main_frame->GetSiteInstance(), subframe2->GetSiteInstance());
1820 EXPECT_NE(subframe1->GetProcess()->GetID(), subframe2->GetProcess()->GetID());
1821 EXPECT_NE(subframe1->GetSiteInstance(), subframe2->GetSiteInstance());
1822 }
1823
1824 // Check that navigating a subframe to an isolated origin error page puts the
1825 // subframe into an OOPIF and its own SiteInstance. Also check that a
1826 // non-isolated error page in a subframe ends up in the correct SiteInstance.
IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,SubframeErrorPages)1827 IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, SubframeErrorPages) {
1828 GURL top_url(
1829 embedded_test_server()->GetURL("/frame_tree/page_with_two_frames.html"));
1830 GURL isolated_url(
1831 embedded_test_server()->GetURL("isolated.foo.com", "/close-socket"));
1832 GURL regular_url(embedded_test_server()->GetURL("a.com", "/close-socket"));
1833
1834 EXPECT_TRUE(NavigateToURL(shell(), top_url));
1835 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
1836 EXPECT_EQ(2u, root->child_count());
1837
1838 FrameTreeNode* child1 = root->child_at(0);
1839 FrameTreeNode* child2 = root->child_at(1);
1840
1841 {
1842 TestFrameNavigationObserver observer(child1);
1843 NavigationHandleObserver handle_observer(web_contents(), isolated_url);
1844 EXPECT_TRUE(ExecuteScript(
1845 child1, "location.href = '" + isolated_url.spec() + "';"));
1846 observer.Wait();
1847 EXPECT_EQ(child1->current_url(), isolated_url);
1848 EXPECT_TRUE(handle_observer.is_error());
1849
1850 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
1851 child1->current_frame_host()->GetSiteInstance());
1852 EXPECT_EQ(GURL("http://isolated.foo.com/"),
1853 child1->current_frame_host()->GetSiteInstance()->GetSiteURL());
1854 }
1855
1856 {
1857 TestFrameNavigationObserver observer(child2);
1858 NavigationHandleObserver handle_observer(web_contents(), regular_url);
1859 EXPECT_TRUE(
1860 ExecuteScript(child2, "location.href = '" + regular_url.spec() + "';"));
1861 observer.Wait();
1862 EXPECT_EQ(child2->current_url(), regular_url);
1863 EXPECT_TRUE(handle_observer.is_error());
1864 if (AreAllSitesIsolatedForTesting()) {
1865 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
1866 child2->current_frame_host()->GetSiteInstance());
1867 EXPECT_EQ(SiteInstance::GetSiteForURL(web_contents()->GetBrowserContext(),
1868 regular_url),
1869 child2->current_frame_host()->GetSiteInstance()->GetSiteURL());
1870 } else {
1871 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
1872 child2->current_frame_host()->GetSiteInstance());
1873 }
1874 EXPECT_NE(GURL(kUnreachableWebDataURL),
1875 child2->current_frame_host()->GetSiteInstance()->GetSiteURL());
1876 }
1877 }
1878
1879 namespace {
HasDefaultSiteInstance(RenderFrameHost * rfh)1880 bool HasDefaultSiteInstance(RenderFrameHost* rfh) {
1881 return static_cast<SiteInstanceImpl*>(rfh->GetSiteInstance())
1882 ->IsDefaultSiteInstance();
1883 }
1884 } // namespace
1885
1886 // Verify process assignment behavior for the case where a site that does not
1887 // require isolation embeds a frame that does require isolation, which in turn
1888 // embeds another site that does not require isolation.
1889 // A (Does not require isolation)
1890 // +-> B (requires isolation)
1891 // +-> C (different site from A that does not require isolation.)
1892 // +-> A (same site as top-level which also does not require isolation.)
IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,AIsolatedCA)1893 IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, AIsolatedCA) {
1894 GURL main_url(embedded_test_server()->GetURL(
1895 "www.foo.com",
1896 "/cross_site_iframe_factory.html?a(isolated.foo.com(c(www.foo.com)))"));
1897 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1898 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
1899 RenderFrameHost* a = root->current_frame_host();
1900 RenderFrameHost* b = root->child_at(0)->current_frame_host();
1901 RenderFrameHost* c = root->child_at(0)->child_at(0)->current_frame_host();
1902 RenderFrameHost* d =
1903 root->child_at(0)->child_at(0)->child_at(0)->current_frame_host();
1904
1905 // Sanity check that the test works with the right frame tree.
1906 EXPECT_FALSE(IsIsolatedOrigin(a->GetLastCommittedOrigin()));
1907 EXPECT_TRUE(IsIsolatedOrigin(b->GetLastCommittedOrigin()));
1908 EXPECT_FALSE(IsIsolatedOrigin(c->GetLastCommittedOrigin()));
1909 EXPECT_FALSE(IsIsolatedOrigin(d->GetLastCommittedOrigin()));
1910 EXPECT_EQ("www.foo.com", a->GetLastCommittedURL().host());
1911 EXPECT_EQ("isolated.foo.com", b->GetLastCommittedURL().host());
1912 EXPECT_EQ("c.com", c->GetLastCommittedURL().host());
1913 EXPECT_EQ("www.foo.com", d->GetLastCommittedURL().host());
1914
1915 // Verify that the isolated site is indeed isolated.
1916 EXPECT_NE(b->GetProcess()->GetID(), a->GetProcess()->GetID());
1917 EXPECT_NE(b->GetProcess()->GetID(), c->GetProcess()->GetID());
1918 EXPECT_NE(b->GetProcess()->GetID(), d->GetProcess()->GetID());
1919
1920 // Verify that same-origin a and d frames share a process. This is
1921 // necessary for correctness - otherwise a and d wouldn't be able to
1922 // synchronously script each other.
1923 EXPECT_EQ(a->GetProcess()->GetID(), d->GetProcess()->GetID());
1924
1925 // Verify that same-origin a and d frames can script each other.
1926 EXPECT_TRUE(ExecuteScript(a, "window.name = 'a';"));
1927 EXPECT_TRUE(ExecuteScript(d, R"(
1928 a = window.open('', 'a');
1929 a.cross_frame_property_test = 'hello from d'; )"));
1930 EXPECT_EQ("hello from d",
1931 EvalJs(a, "window.cross_frame_property_test").ExtractString());
1932
1933 // The test assertions below are not strictly necessary - they just document
1934 // the current behavior. In particular, consolidating www.foo.com and c.com
1935 // sites into the same process is not necessary for correctness.
1936 if (AreAllSitesIsolatedForTesting()) {
1937 // All sites are isolated so we expect foo.com, isolated.foo.com and c.com
1938 // to all be in their own processes.
1939 EXPECT_NE(a->GetProcess()->GetID(), b->GetProcess()->GetID());
1940 EXPECT_NE(a->GetProcess()->GetID(), c->GetProcess()->GetID());
1941 EXPECT_NE(b->GetProcess()->GetID(), c->GetProcess()->GetID());
1942
1943 EXPECT_NE(a->GetSiteInstance(), b->GetSiteInstance());
1944 EXPECT_NE(a->GetSiteInstance(), c->GetSiteInstance());
1945 EXPECT_EQ(a->GetSiteInstance(), d->GetSiteInstance());
1946 EXPECT_NE(b->GetSiteInstance(), c->GetSiteInstance());
1947
1948 EXPECT_FALSE(HasDefaultSiteInstance(a));
1949 EXPECT_FALSE(HasDefaultSiteInstance(b));
1950 EXPECT_FALSE(HasDefaultSiteInstance(c));
1951 } else if (AreDefaultSiteInstancesEnabled()) {
1952 // All sites that are not isolated should be in the same default
1953 // SiteInstance process.
1954 EXPECT_NE(a->GetProcess()->GetID(), b->GetProcess()->GetID());
1955 EXPECT_EQ(a->GetProcess()->GetID(), c->GetProcess()->GetID());
1956
1957 EXPECT_NE(a->GetSiteInstance(), b->GetSiteInstance());
1958 EXPECT_EQ(a->GetSiteInstance(), c->GetSiteInstance());
1959 EXPECT_EQ(a->GetSiteInstance(), d->GetSiteInstance());
1960 EXPECT_NE(b->GetSiteInstance(), c->GetSiteInstance());
1961
1962 EXPECT_TRUE(HasDefaultSiteInstance(a));
1963 EXPECT_FALSE(HasDefaultSiteInstance(b));
1964 } else {
1965 // Documenting current behavior where the top level document doesn't end
1966 // up in a default SiteInstance even though it is not isolated and does not
1967 // require a dedicated process. c.com does get placed in a default
1968 // SiteInstance because we currently allow subframes that don't require
1969 // isolation to share a process. This behavior should go away once we
1970 // turn on default SiteInstances by default.
1971 EXPECT_NE(a->GetProcess()->GetID(), b->GetProcess()->GetID());
1972 EXPECT_NE(a->GetProcess()->GetID(), c->GetProcess()->GetID());
1973
1974 EXPECT_NE(a->GetSiteInstance(), b->GetSiteInstance());
1975 EXPECT_NE(a->GetSiteInstance(), c->GetSiteInstance());
1976 EXPECT_EQ(a->GetSiteInstance(), d->GetSiteInstance());
1977 EXPECT_NE(b->GetSiteInstance(), c->GetSiteInstance());
1978
1979 EXPECT_FALSE(HasDefaultSiteInstance(a));
1980 EXPECT_FALSE(HasDefaultSiteInstance(b));
1981 EXPECT_TRUE(HasDefaultSiteInstance(c));
1982 }
1983 }
1984
IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,NavigateToBlobURL)1985 IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, NavigateToBlobURL) {
1986 GURL top_url(
1987 embedded_test_server()->GetURL("www.foo.com", "/page_with_iframe.html"));
1988 EXPECT_TRUE(NavigateToURL(shell(), top_url));
1989
1990 GURL isolated_url(embedded_test_server()->GetURL("isolated.foo.com",
1991 "/page_with_iframe.html"));
1992
1993 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
1994 FrameTreeNode* child = root->child_at(0);
1995
1996 NavigateIframeToURL(web_contents(), "test_iframe", isolated_url);
1997 EXPECT_EQ(child->current_url(), isolated_url);
1998 EXPECT_TRUE(child->current_frame_host()->IsCrossProcessSubframe());
1999
2000 // Now navigate the child frame to a Blob URL.
2001 TestNavigationObserver load_observer(shell()->web_contents());
2002 EXPECT_TRUE(ExecuteScript(shell()->web_contents()->GetMainFrame(),
2003 "const b = new Blob(['foo']);\n"
2004 "const u = URL.createObjectURL(b);\n"
2005 "frames[0].location = u;\n"
2006 "URL.revokeObjectURL(u);"));
2007 load_observer.Wait();
2008 EXPECT_TRUE(base::StartsWith(child->current_url().spec(),
2009 "blob:http://www.foo.com",
2010 base::CompareCase::SENSITIVE));
2011 EXPECT_TRUE(load_observer.last_navigation_succeeded());
2012 }
2013
2014 // Ensure that --disable-site-isolation-trials disables origin isolation.
2015 class IsolatedOriginTrialOverrideTest : public IsolatedOriginFieldTrialTest {
2016 public:
IsolatedOriginTrialOverrideTest()2017 IsolatedOriginTrialOverrideTest() {}
2018
~IsolatedOriginTrialOverrideTest()2019 ~IsolatedOriginTrialOverrideTest() override {}
2020
SetUpCommandLine(base::CommandLine * command_line)2021 void SetUpCommandLine(base::CommandLine* command_line) override {
2022 command_line->AppendSwitch(switches::kDisableSiteIsolation);
2023 }
2024
2025 private:
2026 DISALLOW_COPY_AND_ASSIGN(IsolatedOriginTrialOverrideTest);
2027 };
2028
IN_PROC_BROWSER_TEST_F(IsolatedOriginTrialOverrideTest,Test)2029 IN_PROC_BROWSER_TEST_F(IsolatedOriginTrialOverrideTest, Test) {
2030 if (AreAllSitesIsolatedForTesting())
2031 return;
2032 EXPECT_FALSE(IsIsolatedOrigin(GURL("https://field.trial.com/")));
2033 EXPECT_FALSE(IsIsolatedOrigin(GURL("https://bar.com/")));
2034 }
2035
2036 // Ensure that --disable-site-isolation-trials and/or
2037 // --disable-site-isolation-for-policy do not override the flag.
2038 class IsolatedOriginPolicyOverrideTest : public IsolatedOriginFieldTrialTest {
2039 public:
IsolatedOriginPolicyOverrideTest()2040 IsolatedOriginPolicyOverrideTest() {}
2041
~IsolatedOriginPolicyOverrideTest()2042 ~IsolatedOriginPolicyOverrideTest() override {}
2043
SetUpCommandLine(base::CommandLine * command_line)2044 void SetUpCommandLine(base::CommandLine* command_line) override {
2045 command_line->AppendSwitch(switches::kDisableSiteIsolation);
2046 #if defined(OS_ANDROID)
2047 command_line->AppendSwitch(switches::kDisableSiteIsolationForPolicy);
2048 #endif
2049 }
2050
2051 private:
2052 DISALLOW_COPY_AND_ASSIGN(IsolatedOriginPolicyOverrideTest);
2053 };
2054
IN_PROC_BROWSER_TEST_F(IsolatedOriginPolicyOverrideTest,Test)2055 IN_PROC_BROWSER_TEST_F(IsolatedOriginPolicyOverrideTest, Test) {
2056 if (AreAllSitesIsolatedForTesting())
2057 return;
2058 EXPECT_FALSE(IsIsolatedOrigin(GURL("https://field.trial.com/")));
2059 EXPECT_FALSE(IsIsolatedOrigin(GURL("https://bar.com/")));
2060 }
2061
2062 // Ensure that --disable-site-isolation-trials and/or
2063 // --disable-site-isolation-for-policy do not override the flag.
2064 class IsolatedOriginNoFlagOverrideTest : public IsolatedOriginTest {
2065 public:
IsolatedOriginNoFlagOverrideTest()2066 IsolatedOriginNoFlagOverrideTest() {}
2067
~IsolatedOriginNoFlagOverrideTest()2068 ~IsolatedOriginNoFlagOverrideTest() override {}
2069
SetUpCommandLine(base::CommandLine * command_line)2070 void SetUpCommandLine(base::CommandLine* command_line) override {
2071 IsolatedOriginTest::SetUpCommandLine(command_line);
2072 command_line->AppendSwitch(switches::kDisableSiteIsolation);
2073 #if defined(OS_ANDROID)
2074 command_line->AppendSwitch(switches::kDisableSiteIsolationForPolicy);
2075 #endif
2076 }
2077
2078 private:
2079 DISALLOW_COPY_AND_ASSIGN(IsolatedOriginNoFlagOverrideTest);
2080 };
2081
IN_PROC_BROWSER_TEST_F(IsolatedOriginNoFlagOverrideTest,Test)2082 IN_PROC_BROWSER_TEST_F(IsolatedOriginNoFlagOverrideTest, Test) {
2083 GURL isolated_url(
2084 embedded_test_server()->GetURL("isolated.foo.com", "/title2.html"));
2085 EXPECT_TRUE(IsIsolatedOrigin(isolated_url));
2086 }
2087
2088 // Verify that main frame's origin isolation still keeps all same-origin frames
2089 // in the same process. When allocating processes for a(b(c),d(c)), we should
2090 // ensure that "c" frames are in the same process.
2091 //
2092 // This is a regression test for https://crbug.com/787576.
IN_PROC_BROWSER_TEST_F(IsolatedOriginNoFlagOverrideTest,SameOriginSubframesProcessSharing)2093 IN_PROC_BROWSER_TEST_F(IsolatedOriginNoFlagOverrideTest,
2094 SameOriginSubframesProcessSharing) {
2095 GURL main_url(embedded_test_server()->GetURL(
2096 "isolated.foo.com", "/cross_site_iframe_factory.html?a(b(c),d(c))"));
2097 EXPECT_TRUE(NavigateToURL(shell(), main_url));
2098 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
2099 RenderFrameHost* a = root->current_frame_host();
2100 RenderFrameHost* b = root->child_at(0)->current_frame_host();
2101 RenderFrameHost* c1 = root->child_at(0)->child_at(0)->current_frame_host();
2102 RenderFrameHost* d = root->child_at(1)->current_frame_host();
2103 RenderFrameHost* c2 = root->child_at(1)->child_at(0)->current_frame_host();
2104
2105 // Sanity check that the test works with the right frame tree.
2106 EXPECT_TRUE(IsIsolatedOrigin(a->GetLastCommittedOrigin()));
2107 EXPECT_FALSE(IsIsolatedOrigin(b->GetLastCommittedOrigin()));
2108 EXPECT_FALSE(IsIsolatedOrigin(d->GetLastCommittedOrigin()));
2109 EXPECT_FALSE(IsIsolatedOrigin(c1->GetLastCommittedOrigin()));
2110 EXPECT_FALSE(IsIsolatedOrigin(c2->GetLastCommittedOrigin()));
2111 EXPECT_EQ("b.com", b->GetLastCommittedURL().host());
2112 EXPECT_EQ("d.com", d->GetLastCommittedURL().host());
2113 EXPECT_EQ("c.com", c1->GetLastCommittedURL().host());
2114 EXPECT_EQ("c.com", c2->GetLastCommittedURL().host());
2115
2116 // Verify that the isolated site is indeed isolated.
2117 EXPECT_NE(a->GetProcess()->GetID(), c1->GetProcess()->GetID());
2118 EXPECT_NE(a->GetProcess()->GetID(), c2->GetProcess()->GetID());
2119 EXPECT_NE(a->GetProcess()->GetID(), b->GetProcess()->GetID());
2120 EXPECT_NE(a->GetProcess()->GetID(), d->GetProcess()->GetID());
2121
2122 // Verify that same-origin c1 and c2 frames share a process. This is
2123 // necessary for correctness - otherwise c1 and c2 wouldn't be able to
2124 // synchronously script each other.
2125 EXPECT_EQ(c1->GetProcess()->GetID(), c2->GetProcess()->GetID());
2126
2127 // Verify that same-origin c1 and c2 frames can script each other.
2128 EXPECT_TRUE(ExecuteScript(c1, "window.name = 'c1';"));
2129 EXPECT_TRUE(ExecuteScript(c2, R"(
2130 c1 = window.open('', 'c1');
2131 c1.cross_frame_property_test = 'hello from c2'; )"));
2132 std::string actual_property_value;
2133 EXPECT_TRUE(ExecuteScriptAndExtractString(
2134 c1, "domAutomationController.send(window.cross_frame_property_test);",
2135 &actual_property_value));
2136 EXPECT_EQ("hello from c2", actual_property_value);
2137
2138 // The test assertions below are not strictly necessary - they just document
2139 // the current behavior and might be tweaked if needed. In particular,
2140 // consolidating b,c,d sites into the same process is not necessary for
2141 // correctness. Consolidation might be desirable if we want to limit the
2142 // number of renderer processes. OTOH, consolidation might be undesirable
2143 // if we desire smaller renderer processes (even if it means more processes).
2144 if (!AreAllSitesIsolatedForTesting()) {
2145 EXPECT_EQ(b->GetProcess()->GetID(), c1->GetProcess()->GetID());
2146 EXPECT_EQ(b->GetProcess()->GetID(), c2->GetProcess()->GetID());
2147 EXPECT_EQ(b->GetProcess()->GetID(), d->GetProcess()->GetID());
2148 } else {
2149 EXPECT_NE(b->GetProcess()->GetID(), c1->GetProcess()->GetID());
2150 EXPECT_NE(b->GetProcess()->GetID(), c2->GetProcess()->GetID());
2151 EXPECT_NE(b->GetProcess()->GetID(), d->GetProcess()->GetID());
2152 EXPECT_EQ(c1->GetProcess()->GetID(), c2->GetProcess()->GetID());
2153 }
2154 }
2155
2156 // Helper class for testing dynamically-added isolated origins. Tests that use
2157 // this run without full --site-per-process, but with two isolated origins that
2158 // are configured at startup (isolated.foo.com and isolated.bar.com).
2159 class DynamicIsolatedOriginTest : public IsolatedOriginTest {
2160 public:
DynamicIsolatedOriginTest()2161 DynamicIsolatedOriginTest()
2162 : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
~DynamicIsolatedOriginTest()2163 ~DynamicIsolatedOriginTest() override {}
2164
SetUpCommandLine(base::CommandLine * command_line)2165 void SetUpCommandLine(base::CommandLine* command_line) override {
2166 IsolatedOriginTest::SetUpCommandLine(command_line);
2167 command_line->AppendSwitch(switches::kDisableSiteIsolation);
2168 // This is necessary to use https with arbitrary hostnames.
2169 command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
2170
2171 if (AreAllSitesIsolatedForTesting()) {
2172 LOG(WARNING) << "This test should be run without strict site isolation. "
2173 << "It does nothing when --site-per-process is specified.";
2174 }
2175 }
2176
SetUpOnMainThread()2177 void SetUpOnMainThread() override {
2178 https_server()->AddDefaultHandlers(GetTestDataFilePath());
2179 ASSERT_TRUE(https_server()->Start());
2180 IsolatedOriginTest::SetUpOnMainThread();
2181 }
2182
2183 // Need an https server because third-party cookies are used, and
2184 // SameSite=None cookies must be Secure.
https_server()2185 net::EmbeddedTestServer* https_server() { return &https_server_; }
2186
2187 private:
2188 net::EmbeddedTestServer https_server_;
2189 DISALLOW_COPY_AND_ASSIGN(DynamicIsolatedOriginTest);
2190 };
2191
2192 // Check that dynamically added isolated origins take effect for future
2193 // BrowsingInstances only.
IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest,IsolationAppliesToFutureBrowsingInstances)2194 IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest,
2195 IsolationAppliesToFutureBrowsingInstances) {
2196 // This test is designed to run without strict site isolation.
2197 if (AreAllSitesIsolatedForTesting())
2198 return;
2199
2200 // Start on a non-isolated origin with same-site iframe.
2201 GURL foo_url(
2202 embedded_test_server()->GetURL("foo.com", "/page_with_iframe.html"));
2203 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
2204
2205 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
2206 FrameTreeNode* child = root->child_at(0);
2207
2208 // Navigate iframe cross-site.
2209 GURL bar_url(embedded_test_server()->GetURL("bar.com", "/title1.html"));
2210 NavigateIframeToURL(web_contents(), "test_iframe", bar_url);
2211 EXPECT_EQ(child->current_url(), bar_url);
2212
2213 // The two frames should be in the same process, since neither site is
2214 // isolated so far.
2215 if (!AreAllSitesIsolatedForTesting()) {
2216 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
2217 child->current_frame_host()->GetSiteInstance());
2218 EXPECT_EQ(root->current_frame_host()->GetProcess(),
2219 child->current_frame_host()->GetProcess());
2220 }
2221
2222 // Start isolating foo.com.
2223 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
2224 policy->AddIsolatedOrigins({url::Origin::Create(foo_url)},
2225 IsolatedOriginSource::TEST);
2226
2227 // The isolation shouldn't take effect in the current frame tree, so that it
2228 // doesn't break same-site scripting. Navigate iframe to a foo.com URL and
2229 // ensure it stays in the same process.
2230 NavigateIframeToURL(web_contents(), "test_iframe", foo_url);
2231 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
2232 child->current_frame_host()->GetSiteInstance());
2233 EXPECT_EQ(root->current_frame_host()->GetProcess(),
2234 child->current_frame_host()->GetProcess());
2235
2236 // Also try a foo(bar(foo)) hierarchy and check that all frames are still in
2237 // the same SiteInstance/process.
2238 GURL bar_with_foo_url(embedded_test_server()->GetURL(
2239 "bar.com", "/cross_site_iframe_factory.html?bar.com(foo.com)"));
2240 NavigateIframeToURL(web_contents(), "test_iframe", bar_with_foo_url);
2241 FrameTreeNode* grandchild = child->child_at(0);
2242 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
2243 child->current_frame_host()->GetSiteInstance());
2244 EXPECT_EQ(child->current_frame_host()->GetSiteInstance(),
2245 grandchild->current_frame_host()->GetSiteInstance());
2246 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
2247 grandchild->current_frame_host()->GetSiteInstance());
2248
2249 // Create an unrelated window, which will be in a new BrowsingInstance.
2250 // Ensure that foo.com becomes an isolated origin in that window. A
2251 // cross-site bar.com subframe on foo.com should now become an OOPIF.
2252 Shell* second_shell = CreateBrowser();
2253 EXPECT_TRUE(NavigateToURL(second_shell, foo_url));
2254
2255 FrameTreeNode* second_root =
2256 static_cast<WebContentsImpl*>(second_shell->web_contents())
2257 ->GetFrameTree()
2258 ->root();
2259 FrameTreeNode* second_child = second_root->child_at(0);
2260
2261 NavigateIframeToURL(second_shell->web_contents(), "test_iframe", bar_url);
2262 scoped_refptr<SiteInstance> foo_instance =
2263 second_root->current_frame_host()->GetSiteInstance();
2264 EXPECT_NE(foo_instance,
2265 second_child->current_frame_host()->GetSiteInstance());
2266 EXPECT_NE(second_root->current_frame_host()->GetProcess(),
2267 second_child->current_frame_host()->GetProcess());
2268
2269 // Now try the reverse: ensure that when bar.com embeds foo.com, foo.com
2270 // becomes an OOPIF.
2271 EXPECT_TRUE(NavigateToURL(second_shell, bar_with_foo_url));
2272
2273 // We should've swapped processes in the main frame, since we navigated from
2274 // (isolated) foo.com to (non-isolated) bar.com.
2275 EXPECT_NE(foo_instance, second_root->current_frame_host()->GetSiteInstance());
2276
2277 // Ensure the new foo.com subframe is cross-process.
2278 second_child = second_root->child_at(0);
2279 EXPECT_NE(second_root->current_frame_host()->GetSiteInstance(),
2280 second_child->current_frame_host()->GetSiteInstance());
2281 EXPECT_NE(second_root->current_frame_host()->GetProcess(),
2282 second_child->current_frame_host()->GetProcess());
2283 }
2284
2285 // Check that dynamically added isolated origins take effect for future
2286 // BrowsingInstances only, focusing on various main frame navigations.
IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest,MainFrameNavigations)2287 IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest, MainFrameNavigations) {
2288 // This test is designed to run without strict site isolation.
2289 if (AreAllSitesIsolatedForTesting())
2290 return;
2291
2292 // Create three windows on a non-isolated origin.
2293 GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
2294 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
2295
2296 Shell* shell2 = CreateBrowser();
2297 EXPECT_TRUE(NavigateToURL(shell2, foo_url));
2298
2299 Shell* shell3 = CreateBrowser();
2300 EXPECT_TRUE(NavigateToURL(shell3, foo_url));
2301
2302 // Create window.open popups in all three windows, which would prevent a
2303 // BrowsingInstance swap on renderer-initiated navigations to newly isolated
2304 // origins in these windows.
2305 OpenPopup(shell(), foo_url, "");
2306 OpenPopup(shell2, GURL(url::kAboutBlankURL), "");
2307 OpenPopup(shell3, embedded_test_server()->GetURL("baz.com", "/title1.html"),
2308 "");
2309
2310 // Start isolating bar.com.
2311 GURL bar_url(embedded_test_server()->GetURL("bar.com", "/title2.html"));
2312 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
2313 policy->AddIsolatedOrigins({url::Origin::Create(bar_url)},
2314 IsolatedOriginSource::TEST);
2315
2316 // Do a renderer-initiated navigation in each of the existing three windows.
2317 // None of them should swap to a new process, since bar.com shouldn't be
2318 // isolated in those older BrowsingInstances.
2319 int old_process_id = web_contents()->GetMainFrame()->GetProcess()->GetID();
2320 EXPECT_TRUE(NavigateToURLFromRenderer(shell(), bar_url));
2321 EXPECT_EQ(old_process_id,
2322 web_contents()->GetMainFrame()->GetProcess()->GetID());
2323
2324 old_process_id =
2325 shell2->web_contents()->GetMainFrame()->GetProcess()->GetID();
2326 EXPECT_TRUE(NavigateToURLFromRenderer(shell2, bar_url));
2327 EXPECT_EQ(old_process_id,
2328 shell2->web_contents()->GetMainFrame()->GetProcess()->GetID());
2329
2330 old_process_id =
2331 shell3->web_contents()->GetMainFrame()->GetProcess()->GetID();
2332 EXPECT_TRUE(NavigateToURLFromRenderer(shell3, bar_url));
2333 EXPECT_EQ(old_process_id,
2334 shell3->web_contents()->GetMainFrame()->GetProcess()->GetID());
2335
2336 // Now try the same in a new window and BrowsingInstance, and ensure that the
2337 // navigation to bar.com swaps processes in that case.
2338 Shell* shell4 = CreateBrowser();
2339 EXPECT_TRUE(NavigateToURL(shell4, foo_url));
2340
2341 old_process_id =
2342 shell4->web_contents()->GetMainFrame()->GetProcess()->GetID();
2343 EXPECT_TRUE(NavigateToURLFromRenderer(shell4, bar_url));
2344 EXPECT_NE(old_process_id,
2345 shell4->web_contents()->GetMainFrame()->GetProcess()->GetID());
2346
2347 // Go back to foo.com in window 1, ensuring this stays in the same process.
2348 {
2349 old_process_id = web_contents()->GetMainFrame()->GetProcess()->GetID();
2350 TestNavigationObserver back_observer(web_contents());
2351 web_contents()->GetController().GoBack();
2352 back_observer.Wait();
2353 EXPECT_EQ(old_process_id,
2354 web_contents()->GetMainFrame()->GetProcess()->GetID());
2355 }
2356
2357 // Go back to foo.com in window 4, ensuring this swaps processes.
2358 {
2359 old_process_id =
2360 shell4->web_contents()->GetMainFrame()->GetProcess()->GetID();
2361 TestNavigationObserver back_observer(shell4->web_contents());
2362 shell4->web_contents()->GetController().GoBack();
2363 back_observer.Wait();
2364 EXPECT_NE(old_process_id,
2365 shell4->web_contents()->GetMainFrame()->GetProcess()->GetID());
2366 }
2367 }
2368
2369 // Check that dynamically added isolated origins do not prevent older processes
2370 // for the same origin from accessing cookies.
IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest,OldProcessCanAccessCookies)2371 IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest, OldProcessCanAccessCookies) {
2372 // This test is designed to run without strict site isolation.
2373 if (AreAllSitesIsolatedForTesting())
2374 return;
2375
2376 GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
2377 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
2378 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
2379
2380 // Since foo.com isn't isolated yet, its process shouldn't be locked to
2381 // anything.
2382 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
2383 EXPECT_EQ(GURL(), policy->GetOriginLock(
2384 root->current_frame_host()->GetProcess()->GetID()));
2385
2386 // Start isolating foo.com.
2387 policy->AddIsolatedOrigins({url::Origin::Create(foo_url)},
2388 IsolatedOriginSource::TEST);
2389
2390 // Create an unrelated window, which will be in a new BrowsingInstance.
2391 // foo.com will become an isolated origin in that window.
2392 Shell* second_shell = CreateBrowser();
2393 EXPECT_TRUE(NavigateToURL(second_shell, foo_url));
2394 FrameTreeNode* second_root =
2395 static_cast<WebContentsImpl*>(second_shell->web_contents())
2396 ->GetFrameTree()
2397 ->root();
2398
2399 // The new window's process should be locked to "foo.com".
2400 int isolated_foo_com_process_id =
2401 second_root->current_frame_host()->GetProcess()->GetID();
2402 EXPECT_EQ(GURL("http://foo.com"),
2403 policy->GetOriginLock(isolated_foo_com_process_id));
2404
2405 // Make sure both old and new foo.com processes can access cookies without
2406 // renderer kills.
2407 EXPECT_TRUE(ExecuteScript(root, "document.cookie = 'foo=bar';"));
2408 EXPECT_EQ("foo=bar", EvalJs(root, "document.cookie"));
2409 EXPECT_TRUE(ExecuteScript(second_root, "document.cookie = 'foo=bar';"));
2410 EXPECT_EQ("foo=bar", EvalJs(second_root, "document.cookie"));
2411
2412 // Navigate to sub.foo.com in |second_shell|, staying in same
2413 // BrowsingInstance. This should stay in the same process.
2414 GURL sub_foo_url(
2415 embedded_test_server()->GetURL("sub.foo.com", "/title1.html"));
2416 EXPECT_TRUE(NavigateToURLInSameBrowsingInstance(second_shell, sub_foo_url));
2417 EXPECT_EQ(isolated_foo_com_process_id,
2418 second_root->current_frame_host()->GetProcess()->GetID());
2419
2420 // Now, start isolating sub.foo.com.
2421 policy->AddIsolatedOrigins({url::Origin::Create(sub_foo_url)},
2422 IsolatedOriginSource::TEST);
2423
2424 // Make sure the process locked to foo.com, which currently has sub.foo.com
2425 // committed in it, can still access sub.foo.com cookies.
2426 EXPECT_TRUE(ExecuteScript(second_root, "document.cookie = 'foo=baz';"));
2427 EXPECT_EQ("foo=baz", EvalJs(second_root, "document.cookie"));
2428
2429 // Now, navigate to sub.foo.com in a new BrowsingInstance. This should go
2430 // into a new process, locked to sub.foo.com.
2431 // TODO(alexmos): navigating to bar.com prior to navigating to sub.foo.com is
2432 // currently needed since we only swap BrowsingInstances on cross-site
2433 // address bar navigations. We should look into swapping BrowsingInstances
2434 // even on same-site browser-initiated navigations, in cases where the sites
2435 // change due to a dynamically isolated origin.
2436 EXPECT_TRUE(NavigateToURL(
2437 second_shell, embedded_test_server()->GetURL("bar.com", "/title2.html")));
2438 EXPECT_TRUE(NavigateToURL(second_shell, sub_foo_url));
2439 EXPECT_NE(isolated_foo_com_process_id,
2440 second_root->current_frame_host()->GetProcess()->GetID());
2441 EXPECT_EQ(GURL("http://sub.foo.com"),
2442 policy->GetOriginLock(
2443 second_root->current_frame_host()->GetProcess()->GetID()));
2444
2445 // Make sure that process can also access sub.foo.com cookies.
2446 EXPECT_TRUE(ExecuteScript(second_root, "document.cookie = 'foo=qux';"));
2447 EXPECT_EQ("foo=qux", EvalJs(second_root, "document.cookie"));
2448 }
2449
2450 // Verify that when isolating sub.foo.com dynamically, foo.com and sub.foo.com
2451 // start to be treated as cross-site for process model decisions.
IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest,IsolatedSubdomain)2452 IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest, IsolatedSubdomain) {
2453 // This test is designed to run without strict site isolation.
2454 if (AreAllSitesIsolatedForTesting())
2455 return;
2456
2457 GURL foo_url(
2458 embedded_test_server()->GetURL("foo.com", "/page_with_iframe.html"));
2459 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
2460
2461 // Start isolating sub.foo.com.
2462 GURL sub_foo_url(
2463 embedded_test_server()->GetURL("sub.foo.com", "/title1.html"));
2464 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
2465 policy->AddIsolatedOrigins({url::Origin::Create(sub_foo_url)},
2466 IsolatedOriginSource::TEST);
2467
2468 // Navigate to foo.com and then to sub.foo.com in a new BrowsingInstance.
2469 // foo.com and sub.foo.com should now be considered cross-site for the
2470 // purposes of process assignment, and we should swap processes.
2471 Shell* new_shell = CreateBrowser();
2472 EXPECT_TRUE(NavigateToURL(new_shell, foo_url));
2473 int initial_process_id =
2474 new_shell->web_contents()->GetMainFrame()->GetProcess()->GetID();
2475 EXPECT_TRUE(NavigateToURLFromRenderer(new_shell, sub_foo_url));
2476 EXPECT_NE(initial_process_id,
2477 new_shell->web_contents()->GetMainFrame()->GetProcess()->GetID());
2478
2479 // Repeat this, but now navigate a subframe on foo.com to sub.foo.com and
2480 // ensure that it is rendered in an OOPIF.
2481 new_shell = CreateBrowser();
2482 EXPECT_TRUE(NavigateToURL(new_shell, foo_url));
2483 NavigateIframeToURL(new_shell->web_contents(), "test_iframe", sub_foo_url);
2484 FrameTreeNode* root = static_cast<WebContentsImpl*>(new_shell->web_contents())
2485 ->GetFrameTree()
2486 ->root();
2487 FrameTreeNode* child = root->child_at(0);
2488
2489 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
2490 child->current_frame_host()->GetSiteInstance());
2491 EXPECT_NE(root->current_frame_host()->GetProcess(),
2492 child->current_frame_host()->GetProcess());
2493 }
2494
2495 // Check that when an isolated origin takes effect in BrowsingInstance 1, a new
2496 // BrowsingInstance 2, which reuses an old process from BrowsingInstance 1 for
2497 // its main frame, still applies the isolated origin to its subframe. This
2498 // demonstrates that isolated origins can't be scoped purely based on process
2499 // IDs.
IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest,NewBrowsingInstanceInOldProcess)2500 IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest,
2501 NewBrowsingInstanceInOldProcess) {
2502 // This test is designed to run without strict site isolation.
2503 if (AreAllSitesIsolatedForTesting())
2504 return;
2505
2506 // Force process reuse for main frames in new BrowsingInstances.
2507 RenderProcessHost::SetMaxRendererProcessCount(1);
2508
2509 // Start on a non-isolated origin with same-site iframe.
2510 GURL foo_url(https_server()->GetURL("foo.com", "/page_with_iframe.html"));
2511 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
2512
2513 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
2514 FrameTreeNode* child = root->child_at(0);
2515
2516 // Navigate iframe cross-site.
2517 GURL bar_url(https_server()->GetURL("bar.com", "/title1.html"));
2518 NavigateIframeToURL(web_contents(), "test_iframe", bar_url);
2519 EXPECT_EQ(child->current_url(), bar_url);
2520
2521 // The iframe should not be in an OOPIF yet.
2522 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
2523 child->current_frame_host()->GetSiteInstance());
2524 EXPECT_EQ(root->current_frame_host()->GetProcess(),
2525 child->current_frame_host()->GetProcess());
2526
2527 // Start isolating bar.com.
2528 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
2529 policy->AddIsolatedOrigins({url::Origin::Create(bar_url)},
2530 IsolatedOriginSource::TEST);
2531
2532 // Open a new window in a new BrowsingInstance. Navigate to foo.com and
2533 // check that the old foo.com process is reused.
2534 Shell* new_shell = CreateBrowser();
2535 EXPECT_TRUE(NavigateToURL(new_shell, foo_url));
2536 FrameTreeNode* new_root =
2537 static_cast<WebContentsImpl*>(new_shell->web_contents())
2538 ->GetFrameTree()
2539 ->root();
2540 FrameTreeNode* new_child = new_root->child_at(0);
2541
2542 EXPECT_EQ(new_root->current_frame_host()->GetProcess(),
2543 root->current_frame_host()->GetProcess());
2544 EXPECT_NE(new_root->current_frame_host()->GetSiteInstance(),
2545 root->current_frame_host()->GetSiteInstance());
2546 EXPECT_FALSE(
2547 new_root->current_frame_host()->GetSiteInstance()->IsRelatedSiteInstance(
2548 root->current_frame_host()->GetSiteInstance()));
2549
2550 // Navigate iframe in the second window to bar.com, and check that it becomes
2551 // an OOPIF in its own process.
2552 NavigateIframeToURL(new_shell->web_contents(), "test_iframe", bar_url);
2553 EXPECT_EQ(new_child->current_url(), bar_url);
2554
2555 EXPECT_NE(new_child->current_frame_host()->GetProcess(),
2556 new_root->current_frame_host()->GetProcess());
2557 EXPECT_NE(new_child->current_frame_host()->GetProcess(),
2558 root->current_frame_host()->GetProcess());
2559 EXPECT_NE(new_child->current_frame_host()->GetProcess(),
2560 child->current_frame_host()->GetProcess());
2561
2562 EXPECT_NE(new_child->current_frame_host()->GetSiteInstance(),
2563 new_root->current_frame_host()->GetSiteInstance());
2564 EXPECT_NE(new_child->current_frame_host()->GetSiteInstance(),
2565 child->current_frame_host()->GetSiteInstance());
2566
2567 // Make sure the bar.com iframe in the old foo.com process can still access
2568 // bar.com cookies.
2569 EXPECT_TRUE(ExecuteScript(
2570 child, "document.cookie = 'foo=bar;SameSite=None;Secure';"));
2571 EXPECT_EQ("foo=bar", EvalJs(child, "document.cookie"));
2572 }
2573
2574 // Verify that a process locked to foo.com is not reused for a navigation to
2575 // foo.com that does not require a dedicated process. See
2576 // https://crbug.com/950453.
IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest,LockedProcessNotReusedForNonisolatedSameSiteNavigation)2577 IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest,
2578 LockedProcessNotReusedForNonisolatedSameSiteNavigation) {
2579 // This test is designed to run without strict site isolation.
2580 if (AreAllSitesIsolatedForTesting())
2581 return;
2582
2583 // Set the process limit to 1.
2584 RenderProcessHost::SetMaxRendererProcessCount(1);
2585
2586 // Start on a non-isolated foo.com URL.
2587 GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
2588 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
2589
2590 // Navigate to a different isolated origin and wait for the original foo.com
2591 // process to shut down. Note that the foo.com SiteInstance will stick
2592 // around in session history.
2593 RenderProcessHostWatcher foo_process_observer(
2594 web_contents()->GetMainFrame()->GetProcess(),
2595 RenderProcessHostWatcher::WATCH_FOR_HOST_DESTRUCTION);
2596 GURL isolated_bar_url(
2597 embedded_test_server()->GetURL("isolated.bar.com", "/title1.html"));
2598 EXPECT_TRUE(NavigateToURL(shell(), isolated_bar_url));
2599 foo_process_observer.Wait();
2600 EXPECT_TRUE(foo_process_observer.did_exit_normally());
2601
2602 // Start isolating foo.com.
2603 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
2604 policy->AddIsolatedOrigins({url::Origin::Create(foo_url)},
2605 IsolatedOriginSource::TEST);
2606
2607 // Create a new window, forcing a new BrowsingInstance, and navigate it to
2608 // foo.com, which will spin up a process locked to foo.com.
2609 Shell* new_shell = CreateBrowser();
2610 EXPECT_TRUE(NavigateToURL(new_shell, foo_url));
2611 RenderProcessHost* new_process =
2612 new_shell->web_contents()->GetMainFrame()->GetProcess();
2613 EXPECT_EQ(GURL("http://foo.com"),
2614 policy->GetOriginLock(new_process->GetID()));
2615
2616 // Go to foo.com in the older first tab, where foo.com does not require a
2617 // dedicated process. Ensure that the existing locked foo.com process is
2618 // *not* reused in that case (if that were the case, LockToOriginIfNeeded
2619 // would trigger a CHECK here). Using a history navigation here ensures that
2620 // the SiteInstance (from session history) will have a foo.com site URL,
2621 // rather than a default site URL, since this case isn't yet handled by the
2622 // default SiteInstance (see crbug.com/787576).
2623 TestNavigationObserver observer(web_contents());
2624 web_contents()->GetController().GoBack();
2625 observer.Wait();
2626 EXPECT_NE(web_contents()->GetMainFrame()->GetProcess(), new_process);
2627 }
2628
2629 // Checks that isolated origins can be added only for a specific profile,
2630 // and that they don't apply to other profiles.
IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest,PerProfileIsolation)2631 IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest, PerProfileIsolation) {
2632 // This test is designed to run without strict site isolation.
2633 if (AreAllSitesIsolatedForTesting())
2634 return;
2635
2636 // Create a browser in a different profile.
2637 BrowserContext* main_context = shell()->web_contents()->GetBrowserContext();
2638 Shell* other_shell = CreateOffTheRecordBrowser();
2639 BrowserContext* other_context =
2640 other_shell->web_contents()->GetBrowserContext();
2641 ASSERT_NE(main_context, other_context);
2642
2643 // Start on bar.com in both browsers.
2644 GURL bar_url(embedded_test_server()->GetURL("bar.com", "/title1.html"));
2645 EXPECT_TRUE(NavigateToURL(shell(), bar_url));
2646 EXPECT_TRUE(NavigateToURL(other_shell, bar_url));
2647
2648 // Start isolating foo.com in |other_context| only.
2649 GURL foo_url(
2650 embedded_test_server()->GetURL("foo.com", "/page_with_iframe.html"));
2651 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
2652 policy->AddIsolatedOrigins({url::Origin::Create(foo_url)},
2653 IsolatedOriginSource::TEST, other_context);
2654
2655 // Verify that foo.com is indeed isolated in |other_shell|, by navigating to
2656 // it in a new BrowsingInstance and checking that a bar.com subframe becomes
2657 // an OOPIF.
2658 EXPECT_TRUE(NavigateToURL(other_shell, foo_url));
2659 WebContentsImpl* other_contents =
2660 static_cast<WebContentsImpl*>(other_shell->web_contents());
2661 NavigateIframeToURL(other_contents, "test_iframe", bar_url);
2662 FrameTreeNode* root = other_contents->GetFrameTree()->root();
2663 FrameTreeNode* child = root->child_at(0);
2664 EXPECT_EQ(child->current_url(), bar_url);
2665 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
2666 child->current_frame_host()->GetSiteInstance());
2667 EXPECT_NE(root->current_frame_host()->GetProcess(),
2668 child->current_frame_host()->GetProcess());
2669
2670 // Verify that foo.com is *not* isolated in the regular shell, due to a
2671 // different profile.
2672 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
2673 NavigateIframeToURL(web_contents(), "test_iframe", bar_url);
2674 root = web_contents()->GetFrameTree()->root();
2675 child = root->child_at(0);
2676 EXPECT_EQ(child->current_url(), bar_url);
2677 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
2678 child->current_frame_host()->GetSiteInstance());
2679 EXPECT_EQ(root->current_frame_host()->GetProcess(),
2680 child->current_frame_host()->GetProcess());
2681 }
2682
2683 // Check that a dynamically added isolated origin can take effect on the next
2684 // main frame navigation by forcing a BrowsingInstance swap, in the case that
2685 // there are no script references to the frame being navigated.
IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest,ForceBrowsingInstanceSwap)2686 IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest, ForceBrowsingInstanceSwap) {
2687 // This test is designed to run without strict site isolation.
2688 if (AreAllSitesIsolatedForTesting())
2689 return;
2690
2691 // Navigate to a non-isolated page with a cross-site iframe. The frame
2692 // shouldn't be in an OOPIF.
2693 GURL foo_url(embedded_test_server()->GetURL(
2694 "foo.com", "/cross_site_iframe_factory.html?foo.com(bar.com)"));
2695 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
2696 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
2697 FrameTreeNode* child = root->child_at(0);
2698 scoped_refptr<SiteInstance> first_instance =
2699 root->current_frame_host()->GetSiteInstance();
2700 EXPECT_EQ(first_instance, child->current_frame_host()->GetSiteInstance());
2701 EXPECT_EQ(root->current_frame_host()->GetProcess(),
2702 child->current_frame_host()->GetProcess());
2703 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
2704 EXPECT_EQ(GURL(),
2705 policy->GetOriginLock(first_instance->GetProcess()->GetID()));
2706
2707 // Start isolating foo.com.
2708 BrowserContext* context = shell()->web_contents()->GetBrowserContext();
2709 policy->AddIsolatedOrigins({url::Origin::Create(foo_url)},
2710 IsolatedOriginSource::TEST, context);
2711
2712 // Try navigating to another foo URL.
2713 GURL foo2_url(embedded_test_server()->GetURL(
2714 "foo.com", "/cross_site_iframe_factory.html?foo.com(baz.com)"));
2715 EXPECT_TRUE(NavigateToURL(shell(), foo2_url));
2716
2717 // Verify that this navigation ended up in a dedicated process, and that we
2718 // swapped BrowsingInstances in the process.
2719 scoped_refptr<SiteInstance> second_instance =
2720 root->current_frame_host()->GetSiteInstance();
2721 EXPECT_NE(first_instance, second_instance);
2722 EXPECT_FALSE(first_instance->IsRelatedSiteInstance(second_instance.get()));
2723 EXPECT_NE(first_instance->GetProcess(), second_instance->GetProcess());
2724 EXPECT_EQ(GURL("http://foo.com"),
2725 policy->GetOriginLock(second_instance->GetProcess()->GetID()));
2726
2727 // The frame on that page should now be an OOPIF.
2728 child = root->child_at(0);
2729 EXPECT_NE(second_instance, child->current_frame_host()->GetSiteInstance());
2730 EXPECT_NE(root->current_frame_host()->GetProcess(),
2731 child->current_frame_host()->GetProcess());
2732 }
2733
2734 // Same as the test above, but using a renderer-initiated navigation. Check
2735 // that a dynamically added isolated origin can take effect on the next main
2736 // frame navigation by forcing a BrowsingInstance swap, in the case that there
2737 // are no script references to the frame being navigated.
IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest,ForceBrowsingInstanceSwap_RendererInitiated)2738 IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest,
2739 ForceBrowsingInstanceSwap_RendererInitiated) {
2740 // This test is designed to run without strict site isolation.
2741 if (AreAllSitesIsolatedForTesting())
2742 return;
2743
2744 // Navigate to a foo.com page.
2745 GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
2746 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
2747 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
2748 scoped_refptr<SiteInstance> first_instance =
2749 root->current_frame_host()->GetSiteInstance();
2750 EXPECT_FALSE(first_instance->RequiresDedicatedProcess());
2751 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
2752 EXPECT_EQ(GURL(),
2753 policy->GetOriginLock(first_instance->GetProcess()->GetID()));
2754
2755 // Set a sessionStorage value, to sanity check that foo.com's session storage
2756 // will still be accessible after the BrowsingInstance swap.
2757 EXPECT_TRUE(ExecJs(root, "window.sessionStorage['foo'] = 'bar';"));
2758
2759 // Start isolating foo.com.
2760 BrowserContext* context = shell()->web_contents()->GetBrowserContext();
2761 policy->AddIsolatedOrigins({url::Origin::Create(foo_url)},
2762 IsolatedOriginSource::TEST, context);
2763
2764 // Do a renderer-initiated navigation to another foo URL.
2765 GURL foo2_url(embedded_test_server()->GetURL(
2766 "foo.com", "/cross_site_iframe_factory.html?foo.com(baz.com)"));
2767 EXPECT_TRUE(NavigateToURLFromRenderer(shell(), foo2_url));
2768
2769 // Verify that this navigation ended up in a dedicated process, and that we
2770 // swapped BrowsingInstances in the process.
2771 scoped_refptr<SiteInstance> second_instance =
2772 root->current_frame_host()->GetSiteInstance();
2773 EXPECT_NE(first_instance, second_instance);
2774 EXPECT_FALSE(first_instance->IsRelatedSiteInstance(second_instance.get()));
2775 EXPECT_NE(first_instance->GetProcess(), second_instance->GetProcess());
2776 EXPECT_EQ(GURL("http://foo.com"),
2777 policy->GetOriginLock(second_instance->GetProcess()->GetID()));
2778
2779 // The frame on that page should be an OOPIF.
2780 FrameTreeNode* child = root->child_at(0);
2781 EXPECT_NE(second_instance, child->current_frame_host()->GetSiteInstance());
2782 EXPECT_NE(root->current_frame_host()->GetProcess(),
2783 child->current_frame_host()->GetProcess());
2784
2785 // Verify that the isolated foo.com page can still access session storage set
2786 // by the previous foo.com page.
2787 EXPECT_EQ("bar", EvalJs(root, "window.sessionStorage['foo']"));
2788 }
2789
IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest,DontForceBrowsingInstanceSwapWhenScriptReferencesExist)2790 IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest,
2791 DontForceBrowsingInstanceSwapWhenScriptReferencesExist) {
2792 // This test is designed to run without strict site isolation.
2793 if (AreAllSitesIsolatedForTesting())
2794 return;
2795
2796 // Navigate to a page that won't be in a dedicated process.
2797 GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
2798 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
2799 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
2800 scoped_refptr<SiteInstance> first_instance =
2801 root->current_frame_host()->GetSiteInstance();
2802 EXPECT_FALSE(first_instance->RequiresDedicatedProcess());
2803
2804 // Start isolating foo.com.
2805 BrowserContext* context = shell()->web_contents()->GetBrowserContext();
2806 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
2807 policy->AddIsolatedOrigins({url::Origin::Create(foo_url)},
2808 IsolatedOriginSource::TEST, context);
2809
2810 // Open a popup.
2811 GURL popup_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
2812 OpenPopup(shell(), popup_url, "");
2813
2814 // Try navigating the main frame to another foo URL.
2815 GURL foo2_url(embedded_test_server()->GetURL("foo.com", "/title2.html"));
2816 EXPECT_TRUE(NavigateToURLFromRenderer(shell(), foo2_url));
2817
2818 // This navigation should not end up in a dedicated process. The popup
2819 // should prevent the BrowsingInstance swap heuristic from applying, since it
2820 // should still be able to communicate with the opener after the navigation.
2821 EXPECT_EQ(first_instance, root->current_frame_host()->GetSiteInstance());
2822 EXPECT_FALSE(first_instance->RequiresDedicatedProcess());
2823 EXPECT_EQ(GURL(),
2824 policy->GetOriginLock(first_instance->GetProcess()->GetID()));
2825 }
2826
2827 // This test ensures that when a page becomes isolated in the middle of
2828 // creating and navigating a new window, the new window prevents a
2829 // BrowsingInstance swap.
IN_PROC_BROWSER_TEST_F(DynamicIsolatedOriginTest,DontForceBrowsingInstanceSwapWithPendingNavigationInNewWindow)2830 IN_PROC_BROWSER_TEST_F(
2831 DynamicIsolatedOriginTest,
2832 DontForceBrowsingInstanceSwapWithPendingNavigationInNewWindow) {
2833 // This test is designed to run without strict site isolation.
2834 if (AreAllSitesIsolatedForTesting())
2835 return;
2836
2837 // Navigate to a page that won't be in a dedicated process.
2838 GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
2839 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
2840 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
2841 scoped_refptr<SiteInstance> first_instance =
2842 root->current_frame_host()->GetSiteInstance();
2843 EXPECT_FALSE(first_instance->RequiresDedicatedProcess());
2844
2845 // Open and start navigating a popup to a URL that never finishes loading.
2846 GURL popup_url(embedded_test_server()->GetURL("a.com", "/hung"));
2847 EXPECT_TRUE(ExecuteScript(root, JsReplace("window.open($1);", popup_url)));
2848
2849 // Start isolating foo.com.
2850 BrowserContext* context = shell()->web_contents()->GetBrowserContext();
2851 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
2852 policy->AddIsolatedOrigins({url::Origin::Create(foo_url)},
2853 IsolatedOriginSource::TEST, context);
2854
2855 // Navigate the main frame to another foo URL.
2856 GURL foo2_url(embedded_test_server()->GetURL("foo.com", "/title2.html"));
2857 EXPECT_TRUE(NavigateToURLFromRenderer(shell(), foo2_url));
2858
2859 // This navigation should not end up in a dedicated process. The pending
2860 // navigation in the popup should prevent the BrowsingInstance swap heuristic
2861 // from applying, since it should still be able to communicate with the
2862 // opener after the navigation.
2863 EXPECT_EQ(first_instance, root->current_frame_host()->GetSiteInstance());
2864 EXPECT_FALSE(first_instance->RequiresDedicatedProcess());
2865 EXPECT_EQ(GURL(),
2866 policy->GetOriginLock(first_instance->GetProcess()->GetID()));
2867 }
2868
2869 // This class allows intercepting the BroadcastChannelProvider::ConnectToChannel
2870 // method and changing the |origin| parameter before passing the call to the
2871 // real implementation of BroadcastChannelProvider.
2872 class BroadcastChannelProviderInterceptor
2873 : public blink::mojom::BroadcastChannelProviderInterceptorForTesting,
2874 public RenderProcessHostObserver {
2875 public:
BroadcastChannelProviderInterceptor(RenderProcessHostImpl * rph,mojo::PendingReceiver<blink::mojom::BroadcastChannelProvider> receiver,const url::Origin & origin_to_inject)2876 BroadcastChannelProviderInterceptor(
2877 RenderProcessHostImpl* rph,
2878 mojo::PendingReceiver<blink::mojom::BroadcastChannelProvider> receiver,
2879 const url::Origin& origin_to_inject)
2880 : origin_to_inject_(origin_to_inject) {
2881 StoragePartitionImpl* storage_partition =
2882 static_cast<StoragePartitionImpl*>(rph->GetStoragePartition());
2883
2884 // Bind the real BroadcastChannelProvider implementation.
2885 mojo::ReceiverId receiver_id =
2886 storage_partition->GetBroadcastChannelProvider()->Connect(
2887 ChildProcessSecurityPolicyImpl::GetInstance()->CreateHandle(
2888 rph->GetID()),
2889 std::move(receiver));
2890
2891 // Now replace it with this object and keep a pointer to the real
2892 // implementation.
2893 original_broadcast_channel_provider_ =
2894 storage_partition->GetBroadcastChannelProvider()
2895 ->receivers_for_testing()
2896 .SwapImplForTesting(receiver_id, this);
2897
2898 // Register the |this| as a RenderProcessHostObserver, so it can be
2899 // correctly cleaned up when the process exits.
2900 rph->AddObserver(this);
2901 }
2902
2903 // Ensure this object is cleaned up when the process goes away, since it
2904 // is not owned by anyone else.
RenderProcessExited(RenderProcessHost * host,const ChildProcessTerminationInfo & info)2905 void RenderProcessExited(RenderProcessHost* host,
2906 const ChildProcessTerminationInfo& info) override {
2907 host->RemoveObserver(this);
2908 delete this;
2909 }
2910
2911 // Allow all methods that aren't explicitly overridden to pass through
2912 // unmodified.
GetForwardingInterface()2913 blink::mojom::BroadcastChannelProvider* GetForwardingInterface() override {
2914 return original_broadcast_channel_provider_;
2915 }
2916
2917 // Override this method to allow changing the origin. It simulates a
2918 // renderer process sending incorrect data to the browser process, so
2919 // security checks can be tested.
ConnectToChannel(const url::Origin & origin,const std::string & name,mojo::PendingAssociatedRemote<blink::mojom::BroadcastChannelClient> client,mojo::PendingAssociatedReceiver<blink::mojom::BroadcastChannelClient> connection)2920 void ConnectToChannel(
2921 const url::Origin& origin,
2922 const std::string& name,
2923 mojo::PendingAssociatedRemote<blink::mojom::BroadcastChannelClient>
2924 client,
2925 mojo::PendingAssociatedReceiver<blink::mojom::BroadcastChannelClient>
2926 connection) override {
2927 GetForwardingInterface()->ConnectToChannel(
2928 origin_to_inject_, name, std::move(client), std::move(connection));
2929 }
2930
2931 private:
2932 // Keep a pointer to the original implementation of the service, so all
2933 // calls can be forwarded to it.
2934 blink::mojom::BroadcastChannelProvider* original_broadcast_channel_provider_;
2935
2936 url::Origin origin_to_inject_;
2937
2938 DISALLOW_COPY_AND_ASSIGN(BroadcastChannelProviderInterceptor);
2939 };
2940
CreateTestBroadcastChannelProvider(const url::Origin & origin_to_inject,RenderProcessHostImpl * rph,mojo::PendingReceiver<blink::mojom::BroadcastChannelProvider> receiver)2941 void CreateTestBroadcastChannelProvider(
2942 const url::Origin& origin_to_inject,
2943 RenderProcessHostImpl* rph,
2944 mojo::PendingReceiver<blink::mojom::BroadcastChannelProvider> receiver) {
2945 // This object will register as RenderProcessHostObserver, so it will
2946 // clean itself automatically on process exit.
2947 new BroadcastChannelProviderInterceptor(rph, std::move(receiver),
2948 origin_to_inject);
2949 }
2950
2951 // Test verifying that a compromised renderer can't lie about |origin| argument
2952 // passed in the BroadcastChannelProvider::ConnectToChannel IPC message.
IN_PROC_BROWSER_TEST_F(IsolatedOriginTest,BroadcastChannelOriginEnforcement)2953 IN_PROC_BROWSER_TEST_F(IsolatedOriginTest, BroadcastChannelOriginEnforcement) {
2954 auto mismatched_origin = url::Origin::Create(GURL("http://abc.foo.com"));
2955 EXPECT_FALSE(IsIsolatedOrigin(mismatched_origin));
2956 RenderProcessHostImpl::SetBroadcastChannelProviderReceiverHandlerForTesting(
2957 base::BindRepeating(&CreateTestBroadcastChannelProvider,
2958 mismatched_origin));
2959
2960 GURL isolated_url(
2961 embedded_test_server()->GetURL("isolated.foo.com", "/title1.html"));
2962 EXPECT_TRUE(IsIsolatedOrigin(url::Origin::Create(isolated_url)));
2963 EXPECT_TRUE(NavigateToURL(shell(), isolated_url));
2964
2965 content::RenderProcessHostBadIpcMessageWaiter kill_waiter(
2966 shell()->web_contents()->GetMainFrame()->GetProcess());
2967 ExecuteScriptAsync(
2968 shell()->web_contents()->GetMainFrame(),
2969 "window.test_channel = new BroadcastChannel('test_channel');");
2970 EXPECT_EQ(bad_message::RPH_MOJO_PROCESS_ERROR, kill_waiter.Wait());
2971 }
2972
2973 class IsolatedOriginTestWithStrictSiteInstances : public IsolatedOriginTest {
2974 public:
IsolatedOriginTestWithStrictSiteInstances()2975 IsolatedOriginTestWithStrictSiteInstances() {
2976 scoped_feature_list_.InitAndEnableFeature(
2977 features::kProcessSharingWithStrictSiteInstances);
2978 }
~IsolatedOriginTestWithStrictSiteInstances()2979 ~IsolatedOriginTestWithStrictSiteInstances() override {}
2980
SetUpCommandLine(base::CommandLine * command_line)2981 void SetUpCommandLine(base::CommandLine* command_line) override {
2982 IsolatedOriginTest::SetUpCommandLine(command_line);
2983 command_line->AppendSwitch(switches::kDisableSiteIsolation);
2984
2985 if (AreAllSitesIsolatedForTesting()) {
2986 LOG(WARNING) << "This test should be run without strict site isolation. "
2987 << "It does nothing when --site-per-process is specified.";
2988 }
2989 }
2990
2991 private:
2992 base::test::ScopedFeatureList scoped_feature_list_;
2993
2994 DISALLOW_COPY_AND_ASSIGN(IsolatedOriginTestWithStrictSiteInstances);
2995 };
2996
IN_PROC_BROWSER_TEST_F(IsolatedOriginTestWithStrictSiteInstances,NonIsolatedFramesCanShareDefaultProcess)2997 IN_PROC_BROWSER_TEST_F(IsolatedOriginTestWithStrictSiteInstances,
2998 NonIsolatedFramesCanShareDefaultProcess) {
2999 // This test is designed to run without strict site isolation.
3000 if (AreAllSitesIsolatedForTesting())
3001 return;
3002
3003 GURL top_url(
3004 embedded_test_server()->GetURL("/frame_tree/page_with_two_frames.html"));
3005 ASSERT_FALSE(IsIsolatedOrigin(url::Origin::Create(top_url)));
3006 EXPECT_TRUE(NavigateToURL(shell(), top_url));
3007
3008 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
3009 FrameTreeNode* child1 = root->child_at(0);
3010 FrameTreeNode* child2 = root->child_at(1);
3011
3012 GURL bar_url(embedded_test_server()->GetURL("www.bar.com", "/title3.html"));
3013 ASSERT_FALSE(IsIsolatedOrigin(url::Origin::Create(bar_url)));
3014 {
3015 TestFrameNavigationObserver observer(child1);
3016 NavigationHandleObserver handle_observer(web_contents(), bar_url);
3017 EXPECT_TRUE(
3018 ExecuteScript(child1, "location.href = '" + bar_url.spec() + "';"));
3019 observer.Wait();
3020 }
3021
3022 GURL baz_url(embedded_test_server()->GetURL("www.baz.com", "/title3.html"));
3023 ASSERT_FALSE(IsIsolatedOrigin(url::Origin::Create(baz_url)));
3024 {
3025 TestFrameNavigationObserver observer(child2);
3026 NavigationHandleObserver handle_observer(web_contents(), baz_url);
3027 EXPECT_TRUE(
3028 ExecuteScript(child2, "location.href = '" + baz_url.spec() + "';"));
3029 observer.Wait();
3030 }
3031
3032 // All 3 frames are from different sites, so each should have its own
3033 // SiteInstance.
3034 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
3035 child1->current_frame_host()->GetSiteInstance());
3036 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
3037 child2->current_frame_host()->GetSiteInstance());
3038 EXPECT_NE(child1->current_frame_host()->GetSiteInstance(),
3039 child2->current_frame_host()->GetSiteInstance());
3040 EXPECT_EQ(
3041 " Site A ------------ proxies for B C\n"
3042 " |--Site B ------- proxies for A C\n"
3043 " +--Site C ------- proxies for A B\n"
3044 "Where A = http://127.0.0.1/\n"
3045 " B = http://bar.com/\n"
3046 " C = http://baz.com/",
3047 FrameTreeVisualizer().DepictFrameTree(root));
3048
3049 // But none are isolated, so all should share the default process for their
3050 // BrowsingInstance.
3051 RenderProcessHost* host = root->current_frame_host()->GetProcess();
3052 EXPECT_EQ(host, child1->current_frame_host()->GetProcess());
3053 EXPECT_EQ(host, child2->current_frame_host()->GetProcess());
3054 EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()
3055 ->GetOriginLock(host->GetID())
3056 .is_empty());
3057 }
3058
3059 // Creates a non-isolated main frame with an isolated child and non-isolated
3060 // grandchild. With strict site isolation disabled and
3061 // kProcessSharingWithStrictSiteInstances enabled, the main frame and the
3062 // grandchild should be in the same process even though they have different
3063 // SiteInstances.
IN_PROC_BROWSER_TEST_F(IsolatedOriginTestWithStrictSiteInstances,IsolatedChildWithNonIsolatedGrandchild)3064 IN_PROC_BROWSER_TEST_F(IsolatedOriginTestWithStrictSiteInstances,
3065 IsolatedChildWithNonIsolatedGrandchild) {
3066 // This test is designed to run without strict site isolation.
3067 if (AreAllSitesIsolatedForTesting())
3068 return;
3069
3070 GURL top_url(
3071 embedded_test_server()->GetURL("www.foo.com", "/page_with_iframe.html"));
3072 ASSERT_FALSE(IsIsolatedOrigin(url::Origin::Create(top_url)));
3073 EXPECT_TRUE(NavigateToURL(shell(), top_url));
3074
3075 GURL isolated_url(embedded_test_server()->GetURL("isolated.foo.com",
3076 "/page_with_iframe.html"));
3077 ASSERT_TRUE(IsIsolatedOrigin(url::Origin::Create(isolated_url)));
3078
3079 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
3080 FrameTreeNode* child = root->child_at(0);
3081
3082 NavigateIframeToURL(web_contents(), "test_iframe", isolated_url);
3083 EXPECT_EQ(child->current_url(), isolated_url);
3084
3085 // Verify that the child frame is an OOPIF with a different SiteInstance.
3086 EXPECT_NE(web_contents()->GetSiteInstance(),
3087 child->current_frame_host()->GetSiteInstance());
3088 EXPECT_TRUE(child->current_frame_host()->IsCrossProcessSubframe());
3089 EXPECT_EQ(GURL("http://isolated.foo.com/"),
3090 child->current_frame_host()->GetSiteInstance()->GetSiteURL());
3091
3092 // Verify that the isolated frame's subframe (which starts out at a relative
3093 // path) is kept in the isolated parent's SiteInstance.
3094 FrameTreeNode* grandchild = child->child_at(0);
3095 EXPECT_EQ(child->current_frame_host()->GetSiteInstance(),
3096 grandchild->current_frame_host()->GetSiteInstance());
3097
3098 // Navigating the grandchild to www.bar.com should put it into the top
3099 // frame's process, but not its SiteInstance.
3100 GURL non_isolated_url(
3101 embedded_test_server()->GetURL("www.bar.com", "/title3.html"));
3102 ASSERT_FALSE(IsIsolatedOrigin(url::Origin::Create(non_isolated_url)));
3103 TestFrameNavigationObserver observer(grandchild);
3104 EXPECT_TRUE(ExecuteScript(
3105 grandchild, "location.href = '" + non_isolated_url.spec() + "';"));
3106 observer.Wait();
3107 EXPECT_EQ(non_isolated_url, grandchild->current_url());
3108
3109 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
3110 grandchild->current_frame_host()->GetSiteInstance());
3111 EXPECT_NE(child->current_frame_host()->GetSiteInstance(),
3112 grandchild->current_frame_host()->GetSiteInstance());
3113 EXPECT_EQ(root->current_frame_host()->GetProcess(),
3114 grandchild->current_frame_host()->GetProcess());
3115 EXPECT_EQ(
3116 " Site A ------------ proxies for B C\n"
3117 " +--Site B ------- proxies for A C\n"
3118 " +--Site C -- proxies for A B\n"
3119 "Where A = http://foo.com/\n"
3120 " B = http://isolated.foo.com/\n"
3121 " C = http://bar.com/",
3122 FrameTreeVisualizer().DepictFrameTree(root));
3123 }
3124
3125 // Navigate a frame into and out of an isolated origin. This should not
3126 // confuse BrowsingInstance into holding onto a stale default_process_.
IN_PROC_BROWSER_TEST_F(IsolatedOriginTestWithStrictSiteInstances,SubframeNavigatesOutofIsolationThenToIsolation)3127 IN_PROC_BROWSER_TEST_F(IsolatedOriginTestWithStrictSiteInstances,
3128 SubframeNavigatesOutofIsolationThenToIsolation) {
3129 // This test is designed to run without strict site isolation.
3130 if (AreAllSitesIsolatedForTesting())
3131 return;
3132
3133 GURL isolated_url(embedded_test_server()->GetURL("isolated.foo.com",
3134 "/page_with_iframe.html"));
3135 ASSERT_TRUE(IsIsolatedOrigin(url::Origin::Create(isolated_url)));
3136 EXPECT_TRUE(NavigateToURL(shell(), isolated_url));
3137
3138 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
3139 FrameTreeNode* child = root->child_at(0);
3140 EXPECT_EQ(web_contents()->GetSiteInstance(),
3141 child->current_frame_host()->GetSiteInstance());
3142 EXPECT_FALSE(child->current_frame_host()->IsCrossProcessSubframe());
3143
3144 GURL non_isolated_url(
3145 embedded_test_server()->GetURL("www.foo.com", "/title3.html"));
3146 ASSERT_FALSE(IsIsolatedOrigin(url::Origin::Create(non_isolated_url)));
3147 NavigateIframeToURL(web_contents(), "test_iframe", non_isolated_url);
3148 EXPECT_EQ(child->current_url(), non_isolated_url);
3149
3150 // Verify that the child frame is an OOPIF with a different SiteInstance.
3151 EXPECT_NE(web_contents()->GetSiteInstance(),
3152 child->current_frame_host()->GetSiteInstance());
3153 EXPECT_NE(root->current_frame_host()->GetProcess(),
3154 child->current_frame_host()->GetProcess());
3155
3156 // Navigating the child to the isolated origin again.
3157 NavigateIframeToURL(web_contents(), "test_iframe", isolated_url);
3158 EXPECT_EQ(child->current_url(), isolated_url);
3159 EXPECT_EQ(web_contents()->GetSiteInstance(),
3160 child->current_frame_host()->GetSiteInstance());
3161
3162 // And navigate out of the isolated origin one last time.
3163 NavigateIframeToURL(web_contents(), "test_iframe", non_isolated_url);
3164 EXPECT_EQ(child->current_url(), non_isolated_url);
3165 EXPECT_NE(web_contents()->GetSiteInstance(),
3166 child->current_frame_host()->GetSiteInstance());
3167 EXPECT_NE(root->current_frame_host()->GetProcess(),
3168 child->current_frame_host()->GetProcess());
3169 EXPECT_EQ(
3170 " Site A ------------ proxies for B\n"
3171 " +--Site B ------- proxies for A\n"
3172 "Where A = http://isolated.foo.com/\n"
3173 " B = http://foo.com/",
3174 FrameTreeVisualizer().DepictFrameTree(root));
3175 }
3176
3177 // Ensure a popup and its opener can go in the same process, even though
3178 // they have different SiteInstances with kProcessSharingWithStrictSiteInstances
3179 // enabled.
IN_PROC_BROWSER_TEST_F(IsolatedOriginTestWithStrictSiteInstances,NonIsolatedPopup)3180 IN_PROC_BROWSER_TEST_F(IsolatedOriginTestWithStrictSiteInstances,
3181 NonIsolatedPopup) {
3182 // This test is designed to run without strict site isolation.
3183 if (AreAllSitesIsolatedForTesting())
3184 return;
3185
3186 GURL foo_url(
3187 embedded_test_server()->GetURL("www.foo.com", "/page_with_iframe.html"));
3188 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
3189 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
3190
3191 // Open a blank popup.
3192 ShellAddedObserver new_shell_observer;
3193 EXPECT_TRUE(ExecuteScript(root, "window.w = window.open();"));
3194 Shell* new_shell = new_shell_observer.GetShell();
3195
3196 // Have the opener navigate the popup to a non-isolated origin.
3197 GURL isolated_url(
3198 embedded_test_server()->GetURL("www.bar.com", "/title1.html"));
3199 {
3200 TestNavigationManager manager(new_shell->web_contents(), isolated_url);
3201 EXPECT_TRUE(ExecuteScript(
3202 root, "window.w.location.href = '" + isolated_url.spec() + "';"));
3203 manager.WaitForNavigationFinished();
3204 }
3205
3206 // The popup and the opener should not share a SiteInstance, but should
3207 // end up in the same process.
3208 EXPECT_NE(new_shell->web_contents()->GetMainFrame()->GetSiteInstance(),
3209 root->current_frame_host()->GetSiteInstance());
3210 EXPECT_EQ(root->current_frame_host()->GetProcess(),
3211 new_shell->web_contents()->GetMainFrame()->GetProcess());
3212 EXPECT_EQ(
3213 " Site A ------------ proxies for B\n"
3214 " +--Site A ------- proxies for B\n"
3215 "Where A = http://foo.com/\n"
3216 " B = http://bar.com/",
3217 FrameTreeVisualizer().DepictFrameTree(root));
3218 EXPECT_EQ(
3219 " Site A ------------ proxies for B\n"
3220 "Where A = http://bar.com/\n"
3221 " B = http://foo.com/",
3222 FrameTreeVisualizer().DepictFrameTree(
3223 static_cast<WebContentsImpl*>(new_shell->web_contents())
3224 ->GetFrameTree()
3225 ->root()));
3226 }
3227
3228 class WildcardOriginIsolationTest : public IsolatedOriginTestBase {
3229 public:
WildcardOriginIsolationTest()3230 WildcardOriginIsolationTest() {}
~WildcardOriginIsolationTest()3231 ~WildcardOriginIsolationTest() override {}
3232
SetUpCommandLine(base::CommandLine * command_line)3233 void SetUpCommandLine(base::CommandLine* command_line) override {
3234 ASSERT_TRUE(embedded_test_server()->InitializeAndListen());
3235
3236 std::string origin_list =
3237 MakeWildcard(embedded_test_server()->GetURL("isolated.foo.com", "/")) +
3238 "," + embedded_test_server()->GetURL("foo.com", "/").spec();
3239
3240 command_line->AppendSwitchASCII(switches::kIsolateOrigins, origin_list);
3241
3242 // This is needed for this test to run properly on platforms where
3243 // --site-per-process isn't the default, such as Android.
3244 IsolateAllSitesForTesting(command_line);
3245 }
3246
SetUpOnMainThread()3247 void SetUpOnMainThread() override {
3248 host_resolver()->AddRule("*", "127.0.0.1");
3249 embedded_test_server()->StartAcceptingConnections();
3250 }
3251
3252 private:
3253 const char* kAllSubdomainWildcard = "[*.]";
3254
3255 // Calling GetURL() on the embedded test server will escape any '*' characters
3256 // into '%2A', so to create a wildcard origin they must be post-processed to
3257 // have the string '[*.]' inserted at the correct point.
MakeWildcard(GURL url)3258 std::string MakeWildcard(GURL url) {
3259 DCHECK(url.is_valid());
3260 return url.scheme() + url::kStandardSchemeSeparator +
3261 kAllSubdomainWildcard + url.GetContent();
3262 }
3263
3264 DISALLOW_COPY_AND_ASSIGN(WildcardOriginIsolationTest);
3265 };
3266
IN_PROC_BROWSER_TEST_F(WildcardOriginIsolationTest,MainFrameNavigation)3267 IN_PROC_BROWSER_TEST_F(WildcardOriginIsolationTest, MainFrameNavigation) {
3268 GURL a_foo_url(embedded_test_server()->GetURL("a.foo.com", "/title1.html"));
3269 GURL b_foo_url(embedded_test_server()->GetURL("b.foo.com", "/title1.html"));
3270 GURL a_isolated_url(
3271 embedded_test_server()->GetURL("a.isolated.foo.com", "/title1.html"));
3272 GURL b_isolated_url(
3273 embedded_test_server()->GetURL("b.isolated.foo.com", "/title1.html"));
3274
3275 EXPECT_TRUE(IsIsolatedOrigin(a_foo_url));
3276 EXPECT_TRUE(IsIsolatedOrigin(b_foo_url));
3277 EXPECT_TRUE(IsIsolatedOrigin(a_isolated_url));
3278 EXPECT_TRUE(IsIsolatedOrigin(b_isolated_url));
3279
3280 // Navigate in the following order, all within the same shell:
3281 // 1. a_foo_url
3282 // 2. b_foo_url -- check (1) and (2) have the same pid / instance
3283 // 3. a_isolated_url
3284 // 4. b_isolated_url -- check (2), (3) and (4) have distinct pids / instances
3285 // 5. a_foo_url -- check (4) and (5) have distinct pids / instances
3286 // 6. b_foo_url -- check (5) and (6) have the same pid / instance
3287
3288 EXPECT_TRUE(NavigateToURL(shell(), a_foo_url));
3289 int a_foo_pid =
3290 shell()->web_contents()->GetMainFrame()->GetProcess()->GetID();
3291 scoped_refptr<SiteInstance> a_foo_instance =
3292 shell()->web_contents()->GetMainFrame()->GetSiteInstance();
3293
3294 EXPECT_TRUE(NavigateToURL(shell(), b_foo_url));
3295 int b_foo_pid =
3296 shell()->web_contents()->GetMainFrame()->GetProcess()->GetID();
3297 scoped_refptr<SiteInstance> b_foo_instance =
3298 shell()->web_contents()->GetMainFrame()->GetSiteInstance();
3299
3300 // Check that hosts in the wildcard subdomain (but not the wildcard subdomain
3301 // itself) have their processes reused between navigation events.
3302 EXPECT_EQ(a_foo_pid, b_foo_pid);
3303 EXPECT_EQ(a_foo_instance, b_foo_instance);
3304
3305 EXPECT_TRUE(NavigateToURL(shell(), a_isolated_url));
3306 int a_isolated_pid =
3307 shell()->web_contents()->GetMainFrame()->GetProcess()->GetID();
3308 scoped_refptr<SiteInstance> a_isolated_instance =
3309 shell()->web_contents()->GetMainFrame()->GetSiteInstance();
3310
3311 EXPECT_TRUE(NavigateToURL(shell(), b_isolated_url));
3312 int b_isolated_pid =
3313 shell()->web_contents()->GetMainFrame()->GetProcess()->GetID();
3314 scoped_refptr<SiteInstance> b_isolated_instance =
3315 shell()->web_contents()->GetMainFrame()->GetSiteInstance();
3316
3317 // Navigating from a non-wildcard domain to a wildcard domain should result in
3318 // a new process.
3319 EXPECT_NE(b_foo_pid, b_isolated_pid);
3320 EXPECT_NE(b_foo_instance, b_isolated_instance);
3321
3322 // Navigating to another URL within the wildcard domain should always result
3323 // in a new process.
3324 EXPECT_NE(a_isolated_pid, b_isolated_pid);
3325 EXPECT_NE(a_isolated_instance, b_isolated_instance);
3326
3327 EXPECT_TRUE(NavigateToURL(shell(), a_foo_url));
3328 a_foo_pid = shell()->web_contents()->GetMainFrame()->GetProcess()->GetID();
3329 a_foo_instance = shell()->web_contents()->GetMainFrame()->GetSiteInstance();
3330
3331 EXPECT_TRUE(NavigateToURL(shell(), b_foo_url));
3332 b_foo_pid = shell()->web_contents()->GetMainFrame()->GetProcess()->GetID();
3333 b_foo_instance = shell()->web_contents()->GetMainFrame()->GetSiteInstance();
3334
3335 // Navigating from the wildcard subdomain to the isolated subdomain should
3336 // produce a new pid.
3337 EXPECT_NE(a_foo_pid, b_isolated_pid);
3338 EXPECT_NE(a_foo_instance, b_isolated_instance);
3339
3340 // Confirm that navigation events in the isolated domain behave the same as
3341 // before visiting the wildcard subdomain.
3342 EXPECT_EQ(a_foo_pid, b_foo_pid);
3343 EXPECT_EQ(a_foo_instance, b_foo_instance);
3344 }
3345
IN_PROC_BROWSER_TEST_F(WildcardOriginIsolationTest,SubFrameNavigation)3346 IN_PROC_BROWSER_TEST_F(WildcardOriginIsolationTest, SubFrameNavigation) {
3347 GURL url = embedded_test_server()->GetURL(
3348 "a.foo.com",
3349 "/cross_site_iframe_factory.html?a.foo.com("
3350 "isolated.foo.com,b.foo.com("
3351 "b.isolated.foo.com,a.foo.com,a.isolated.com))");
3352
3353 EXPECT_TRUE(NavigateToURL(shell(), url));
3354 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
3355
3356 EXPECT_EQ(
3357 " Site A ------------ proxies for B C D\n"
3358 " |--Site B ------- proxies for A C D\n"
3359 " +--Site A ------- proxies for B C D\n"
3360 " |--Site C -- proxies for A B D\n"
3361 " |--Site A -- proxies for B C D\n"
3362 " +--Site D -- proxies for A B C\n"
3363 "Where A = http://foo.com/\n"
3364 " B = http://isolated.foo.com/\n"
3365 " C = http://b.isolated.foo.com/\n"
3366 " D = http://isolated.com/",
3367 FrameTreeVisualizer().DepictFrameTree(root));
3368 }
3369
3370 } // namespace content
3371