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