1 // Copyright 2018 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 <memory>
6 
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/callback.h"
10 #include "base/memory/ptr_util.h"
11 #include "base/test/bind_test_util.h"
12 #include "base/test/scoped_feature_list.h"
13 #include "build/build_config.h"
14 #include "components/viz/host/host_frame_sink_manager.h"
15 #include "content/browser/compositor/surface_utils.h"
16 #include "content/browser/frame_host/render_frame_host_impl.h"
17 #include "content/browser/frame_host/render_frame_host_manager.h"
18 #include "content/browser/frame_host/render_frame_proxy_host.h"
19 #include "content/browser/portal/portal.h"
20 #include "content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.h"
21 #include "content/browser/renderer_host/input/synthetic_tap_gesture.h"
22 #include "content/browser/renderer_host/render_widget_host_input_event_router.h"
23 #include "content/browser/renderer_host/render_widget_host_view_base.h"
24 #include "content/browser/renderer_host/render_widget_host_view_child_frame.h"
25 #include "content/browser/web_contents/web_contents_impl.h"
26 #include "content/common/frame.mojom-test-utils.h"
27 #include "content/public/browser/site_isolation_policy.h"
28 #include "content/public/browser/web_contents_delegate.h"
29 #include "content/public/common/content_switches.h"
30 #include "content/public/test/accessibility_notification_waiter.h"
31 #include "content/public/test/browser_test_utils.h"
32 #include "content/public/test/content_browser_test.h"
33 #include "content/public/test/content_browser_test_utils.h"
34 #include "content/public/test/hit_test_region_observer.h"
35 #include "content/public/test/navigation_handle_observer.h"
36 #include "content/public/test/test_navigation_observer.h"
37 #include "content/shell/browser/shell.h"
38 #include "content/test/content_browser_test_utils_internal.h"
39 #include "content/test/portal/portal_activated_observer.h"
40 #include "content/test/portal/portal_created_observer.h"
41 #include "content/test/portal/portal_interceptor_for_testing.h"
42 #include "content/test/test_render_frame_host_factory.h"
43 #include "net/dns/mock_host_resolver.h"
44 #include "net/test/embedded_test_server/embedded_test_server.h"
45 #include "testing/gmock/include/gmock/gmock.h"
46 #include "testing/gtest/include/gtest/gtest.h"
47 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
48 #include "third_party/blink/public/common/features.h"
49 #include "third_party/blink/public/mojom/input/focus_type.mojom.h"
50 #include "third_party/blink/public/mojom/portal/portal.mojom.h"
51 #include "url/url_constants.h"
52 
53 using testing::_;
54 
55 namespace content {
56 
57 class PortalBrowserTest : public ContentBrowserTest {
58  protected:
PortalBrowserTest()59   PortalBrowserTest() {}
60 
SetUp()61   void SetUp() override {
62     scoped_feature_list_.InitAndEnableFeature(blink::features::kPortals);
63     ContentBrowserTest::SetUp();
64   }
65 
SetUpCommandLine(base::CommandLine * command_line)66   void SetUpCommandLine(base::CommandLine* command_line) override {
67     command_line->AppendSwitch(switches::kValidateInputEventStream);
68     command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
69                                     "OverscrollCustomization");
70   }
71 
SetUpOnMainThread()72   void SetUpOnMainThread() override {
73     host_resolver()->AddRule("*", "127.0.0.1");
74     ContentBrowserTest::SetUpOnMainThread();
75     ASSERT_TRUE(embedded_test_server()->Start());
76   }
77 
CreatePortalToUrl(WebContentsImpl * host_contents,GURL portal_url,int number_of_navigations=1)78   Portal* CreatePortalToUrl(WebContentsImpl* host_contents,
79                             GURL portal_url,
80                             int number_of_navigations = 1) {
81     EXPECT_GE(number_of_navigations, 1);
82     RenderFrameHostImpl* main_frame = host_contents->GetMainFrame();
83 
84     // Create portal and wait for navigation.
85     PortalCreatedObserver portal_created_observer(main_frame);
86     TestNavigationObserver navigation_observer(nullptr, number_of_navigations);
87     navigation_observer.set_wait_event(
88         TestNavigationObserver::WaitEvent::kNavigationFinished);
89     navigation_observer.StartWatchingNewWebContents();
90     EXPECT_TRUE(
91         ExecJs(main_frame,
92                JsReplace("{"
93                          "  let portal = document.createElement('portal');"
94                          "  portal.src = $1;"
95                          "  document.body.appendChild(portal);"
96                          "}",
97                          portal_url),
98                EXECUTE_SCRIPT_NO_USER_GESTURE));
99     Portal* portal = portal_created_observer.WaitUntilPortalCreated();
100     navigation_observer.StopWatchingNewWebContents();
101 
102     WebContentsImpl* portal_contents = portal->GetPortalContents();
103     EXPECT_TRUE(portal_contents);
104 
105     navigation_observer.WaitForNavigationFinished();
106     EXPECT_TRUE(WaitForLoadStop(portal_contents));
107 
108     return portal;
109   }
110 
111  private:
112   base::test::ScopedFeatureList scoped_feature_list_;
113 };
114 
115 // Tests that the renderer can create a Portal.
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,CreatePortal)116 IN_PROC_BROWSER_TEST_F(PortalBrowserTest, CreatePortal) {
117   EXPECT_TRUE(NavigateToURL(
118       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
119   WebContentsImpl* web_contents_impl =
120       static_cast<WebContentsImpl*>(shell()->web_contents());
121   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
122 
123   PortalCreatedObserver portal_created_observer(main_frame);
124   EXPECT_TRUE(
125       ExecJs(main_frame,
126              "document.body.appendChild(document.createElement('portal'));"));
127   Portal* portal = portal_created_observer.WaitUntilPortalCreated();
128   EXPECT_NE(nullptr, portal);
129 }
130 
131 // This fixture enables CompositeCrossOriginIframes for the following regression
132 // test. Since we're testing stability and not behaviour specific to this flag,
133 // this fixture and its test may be removed upon removal of the
134 // CompositeCrossOriginIframes flag.
135 class PortalCompositeCrossOriginIframesBrowserTest : public PortalBrowserTest {
136  protected:
SetUpCommandLine(base::CommandLine * command_line)137   void SetUpCommandLine(base::CommandLine* command_line) override {
138     PortalBrowserTest::SetUpCommandLine(command_line);
139     scoped_feature_list_.InitAndEnableFeature(
140         blink::features::kCompositeCrossOriginIframes);
141   }
142 
143  private:
144   base::test::ScopedFeatureList scoped_feature_list_;
145 };
146 
147 // Regression test for https://crbug.com/1028269
148 // Tests that the renderer can create a portal without crashing when
149 // CompositeCrossOriginIframes is enabled.
IN_PROC_BROWSER_TEST_F(PortalCompositeCrossOriginIframesBrowserTest,CreatePortal)150 IN_PROC_BROWSER_TEST_F(PortalCompositeCrossOriginIframesBrowserTest,
151                        CreatePortal) {
152   EXPECT_TRUE(NavigateToURL(
153       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
154   WebContentsImpl* web_contents_impl =
155       static_cast<WebContentsImpl*>(shell()->web_contents());
156   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
157 
158   GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
159   Portal* portal = CreatePortalToUrl(web_contents_impl, a_url);
160   EXPECT_NE(nullptr, portal);
161 
162   // Ensure that the renderer is still alive.
163   EXPECT_EQ(true, EvalJs(main_frame, "true"));
164 }
165 
166 // Tests the the renderer can navigate a Portal.
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,NavigatePortal)167 IN_PROC_BROWSER_TEST_F(PortalBrowserTest, NavigatePortal) {
168   EXPECT_TRUE(NavigateToURL(
169       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
170   WebContentsImpl* web_contents_impl =
171       static_cast<WebContentsImpl*>(shell()->web_contents());
172   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
173 
174   // Tests that a portal can navigate by setting its src before appending it to
175   // the DOM.
176   GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
177   Portal* portal = CreatePortalToUrl(web_contents_impl, a_url);
178   WebContents* portal_contents = portal->GetPortalContents();
179   EXPECT_EQ(portal_contents->GetLastCommittedURL(), a_url);
180 
181   // Tests that a portal can navigate by setting its src.
182   {
183     TestNavigationObserver navigation_observer(portal_contents);
184 
185     GURL b_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
186     EXPECT_TRUE(
187         ExecJs(main_frame,
188                JsReplace("document.querySelector('portal').src = $1;", b_url)));
189     navigation_observer.Wait();
190     EXPECT_EQ(navigation_observer.last_navigation_url(), b_url);
191     EXPECT_EQ(portal_contents->GetLastCommittedURL(), b_url);
192   }
193 
194   // Tests that a portal can navigate by setting the attribute src.
195   {
196     TestNavigationObserver navigation_observer(portal_contents);
197 
198     GURL c_url(embedded_test_server()->GetURL("c.com", "/title1.html"));
199     EXPECT_TRUE(ExecJs(
200         main_frame,
201         JsReplace("document.querySelector('portal').setAttribute('src', $1);",
202                   c_url)));
203     navigation_observer.Wait();
204     EXPECT_EQ(navigation_observer.last_navigation_url(), c_url);
205     EXPECT_EQ(portal_contents->GetLastCommittedURL(), c_url);
206   }
207 }
208 
209 // Tests that a portal can be activated.
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,ActivatePortal)210 IN_PROC_BROWSER_TEST_F(PortalBrowserTest, ActivatePortal) {
211   EXPECT_TRUE(NavigateToURL(
212       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
213   WebContentsImpl* web_contents_impl =
214       static_cast<WebContentsImpl*>(shell()->web_contents());
215   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
216 
217   GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
218   Portal* portal = CreatePortalToUrl(web_contents_impl, a_url);
219 
220   // Ensure that the portal WebContents exists and is different from the tab's
221   // WebContents.
222   WebContents* portal_contents = portal->GetPortalContents();
223   EXPECT_NE(nullptr, portal_contents);
224   EXPECT_NE(portal_contents, shell()->web_contents());
225 
226   PortalActivatedObserver activated_observer(portal);
227   ExecuteScriptAsync(main_frame,
228                      "document.querySelector('portal').activate();");
229   activated_observer.WaitForActivate();
230 
231   // After activation, the shell's WebContents should be the previous portal's
232   // WebContents.
233   EXPECT_EQ(portal_contents, shell()->web_contents());
234 
235   EXPECT_EQ(blink::mojom::PortalActivateResult::kPredecessorWillUnload,
236             activated_observer.WaitForActivateResult());
237 }
238 
239 // Tests if a portal can be activated and the predecessor can be adopted.
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,AdoptPredecessor)240 IN_PROC_BROWSER_TEST_F(PortalBrowserTest, AdoptPredecessor) {
241   EXPECT_TRUE(NavigateToURL(
242       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
243   WebContentsImpl* web_contents_impl =
244       static_cast<WebContentsImpl*>(shell()->web_contents());
245   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
246 
247   GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
248   Portal* portal = CreatePortalToUrl(web_contents_impl, a_url);
249 
250   // Ensure that the portal WebContents exists and is different from the tab's
251   // WebContents.
252   WebContentsImpl* portal_contents = portal->GetPortalContents();
253   EXPECT_NE(nullptr, portal_contents);
254   EXPECT_NE(portal_contents, shell()->web_contents());
255 
256   RenderFrameHostImpl* portal_frame = portal_contents->GetMainFrame();
257   EXPECT_TRUE(ExecJs(portal_frame,
258                      "window.addEventListener('portalactivate', e => { "
259                      "  var portal = e.adoptPredecessor(); "
260                      "  document.body.appendChild(portal); "
261                      "});"));
262 
263   {
264     PortalActivatedObserver activated_observer(portal);
265     PortalCreatedObserver adoption_observer(portal_frame);
266     EXPECT_TRUE(ExecJs(main_frame,
267                        "let portal = document.querySelector('portal');"
268                        "portal.activate().then(() => { "
269                        "  document.body.removeChild(portal); "
270                        "});"));
271     EXPECT_EQ(blink::mojom::PortalActivateResult::kPredecessorWasAdopted,
272               activated_observer.WaitForActivateResult());
273     adoption_observer.WaitUntilPortalCreated();
274   }
275   // After activation, the shell's WebContents should be the previous portal's
276   // WebContents.
277   EXPECT_EQ(portal_contents, shell()->web_contents());
278   // The original predecessor WebContents should be adopted as a portal.
279   EXPECT_TRUE(web_contents_impl->IsPortal());
280   EXPECT_EQ(web_contents_impl->GetOuterWebContents(), portal_contents);
281 }
282 
283 // Tests that the RenderFrameProxyHost is created and initialized when the
284 // portal is initialized.
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,RenderFrameProxyHostCreated)285 IN_PROC_BROWSER_TEST_F(PortalBrowserTest, RenderFrameProxyHostCreated) {
286   EXPECT_TRUE(NavigateToURL(
287       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
288   WebContentsImpl* web_contents_impl =
289       static_cast<WebContentsImpl*>(shell()->web_contents());
290 
291   GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
292   Portal* portal = CreatePortalToUrl(web_contents_impl, a_url);
293   WebContentsImpl* portal_contents = portal->GetPortalContents();
294   RenderFrameProxyHost* proxy_host = portal_contents->GetFrameTree()
295                                          ->root()
296                                          ->render_manager()
297                                          ->GetProxyToOuterDelegate();
298   EXPECT_TRUE(proxy_host->is_render_frame_proxy_live());
299 }
300 
301 // Tests that the portal's outer delegate frame tree node and any iframes
302 // inside the portal are deleted when the portal element is removed from the
303 // document.
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,DetachPortal)304 IN_PROC_BROWSER_TEST_F(PortalBrowserTest, DetachPortal) {
305   EXPECT_TRUE(NavigateToURL(
306       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
307   WebContentsImpl* web_contents =
308       static_cast<WebContentsImpl*>(shell()->web_contents());
309   RenderFrameHostImpl* main_frame = web_contents->GetMainFrame();
310 
311   GURL a_url(embedded_test_server()->GetURL(
312       "a.com", "/cross_site_iframe_factory.html?a(a)"));
313   // Wait for a second navigation for the inner iframe.
314   Portal* portal = CreatePortalToUrl(web_contents, a_url, 2);
315 
316   WebContentsImpl* portal_contents = portal->GetPortalContents();
317   FrameTreeNode* portal_main_frame_node =
318       portal_contents->GetFrameTree()->root();
319 
320   // Remove portal from document and wait for frames to be deleted.
321   FrameDeletedObserver fdo1(portal_main_frame_node->render_manager()
322                                 ->GetOuterDelegateNode()
323                                 ->current_frame_host());
324   FrameDeletedObserver fdo2(
325       portal_main_frame_node->child_at(0)->current_frame_host());
326   EXPECT_TRUE(
327       ExecJs(main_frame,
328              "document.body.removeChild(document.querySelector('portal'));"));
329   fdo1.Wait();
330   fdo2.Wait();
331 }
332 
333 // This is for testing how portals interact with input hit testing. It is
334 // parameterized on the kind of viz hit testing used.
335 class PortalHitTestBrowserTest : public PortalBrowserTest {
336  protected:
SetUpCommandLine(base::CommandLine * command_line)337   void SetUpCommandLine(base::CommandLine* command_line) override {
338     PortalBrowserTest::SetUpCommandLine(command_line);
339     IsolateAllSitesForTesting(command_line);
340   }
341 };
342 
343 namespace {
344 
345 // Fails the test if an input event is sent to the given RenderWidgetHost.
346 class FailOnInputEvent : public RenderWidgetHost::InputEventObserver {
347  public:
FailOnInputEvent(RenderWidgetHostImpl * rwh)348   explicit FailOnInputEvent(RenderWidgetHostImpl* rwh)
349       : rwh_(rwh->GetWeakPtr()) {
350     rwh->AddInputEventObserver(this);
351   }
352 
~FailOnInputEvent()353   ~FailOnInputEvent() override {
354     if (rwh_)
355       rwh_->RemoveInputEventObserver(this);
356   }
357 
OnInputEvent(const blink::WebInputEvent & event)358   void OnInputEvent(const blink::WebInputEvent& event) override {
359     FAIL() << "Unexpected " << blink::WebInputEvent::GetName(event.GetType());
360   }
361 
362  private:
363   base::WeakPtr<RenderWidgetHostImpl> rwh_;
364 };
365 
366 }  // namespace
367 
368 // Tests that input events targeting the portal are only received by the parent
369 // renderer.
IN_PROC_BROWSER_TEST_F(PortalHitTestBrowserTest,DispatchInputEvent)370 IN_PROC_BROWSER_TEST_F(PortalHitTestBrowserTest, DispatchInputEvent) {
371   EXPECT_TRUE(NavigateToURL(
372       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
373   WebContentsImpl* web_contents_impl =
374       static_cast<WebContentsImpl*>(shell()->web_contents());
375   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
376 
377   GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
378   Portal* portal = CreatePortalToUrl(web_contents_impl, a_url);
379   WebContentsImpl* portal_contents = portal->GetPortalContents();
380   RenderFrameHostImpl* portal_frame = portal_contents->GetMainFrame();
381   EXPECT_TRUE(static_cast<RenderWidgetHostViewBase*>(portal_frame->GetView())
382                   ->IsRenderWidgetHostViewChildFrame());
383   RenderWidgetHostViewChildFrame* portal_view =
384       static_cast<RenderWidgetHostViewChildFrame*>(portal_frame->GetView());
385   WaitForHitTestData(portal_frame);
386 
387   FailOnInputEvent no_input_to_portal_frame(
388       portal_frame->GetRenderWidgetHost());
389   EXPECT_TRUE(ExecJs(
390       main_frame,
391       "var clicked = false;"
392       "document.querySelector('portal').onmousedown = _ => clicked = true;"));
393   EXPECT_TRUE(ExecJs(portal_frame,
394                      "var clicked = false;"
395                      "document.body.onmousedown = _ => clicked = true;"));
396   EXPECT_EQ(false, EvalJs(main_frame, "clicked"));
397   EXPECT_EQ(false, EvalJs(portal_frame, "clicked"));
398 
399   // Route the mouse event.
400   gfx::Point root_location =
401       portal_view->TransformPointToRootCoordSpace(gfx::Point(5, 5));
402   InputEventAckWaiter waiter(main_frame->GetRenderWidgetHost(),
403                              blink::WebInputEvent::kMouseDown);
404   SimulateRoutedMouseEvent(web_contents_impl, blink::WebInputEvent::kMouseDown,
405                            blink::WebPointerProperties::Button::kLeft,
406                            root_location);
407   waiter.Wait();
408 
409   // Check that the click event was only received by the main frame.
410   EXPECT_EQ(true, EvalJs(main_frame, "clicked"));
411   EXPECT_EQ(false, EvalJs(portal_frame, "clicked"));
412 }
413 
414 // Tests that input events performed over on OOPIF inside a portal are targeted
415 // to the portal's parent.
IN_PROC_BROWSER_TEST_F(PortalHitTestBrowserTest,NoInputToOOPIFInPortal)416 IN_PROC_BROWSER_TEST_F(PortalHitTestBrowserTest, NoInputToOOPIFInPortal) {
417   EXPECT_TRUE(NavigateToURL(
418       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
419   WebContentsImpl* web_contents_impl =
420       static_cast<WebContentsImpl*>(shell()->web_contents());
421   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
422 
423   // Create portal and wait for navigation.
424   // In the case of crbug.com/1002228 , this does not appear to reproduce if the
425   // portal element is too small, so we give it an explicit size.
426   Portal* portal = nullptr;
427   {
428     PortalCreatedObserver portal_created_observer(main_frame);
429     GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
430     TestNavigationObserver navigation_observer(a_url);
431     navigation_observer.StartWatchingNewWebContents();
432     EXPECT_TRUE(ExecJs(
433         main_frame, JsReplace("var portal = document.createElement('portal');"
434                               "portal.src = $1;"
435                               "portal.style.width = '500px';"
436                               "portal.style.height = '500px';"
437                               "portal.style.border = 'solid';"
438                               "document.body.appendChild(portal);",
439                               a_url)));
440     portal = portal_created_observer.WaitUntilPortalCreated();
441     navigation_observer.Wait();
442   }
443   WebContentsImpl* portal_contents = portal->GetPortalContents();
444   RenderFrameHostImpl* portal_frame = portal_contents->GetMainFrame();
445   WaitForHitTestData(portal_frame);
446 
447   // Add an out-of-process iframe to the portal.
448   GURL b_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
449   TestNavigationObserver iframe_navigation_observer(portal_contents);
450   EXPECT_TRUE(ExecJs(portal_frame,
451                      JsReplace("var iframe = document.createElement('iframe');"
452                                "iframe.src = $1;"
453                                "document.body.appendChild(iframe);",
454                                b_url)));
455   iframe_navigation_observer.Wait();
456   EXPECT_EQ(b_url, iframe_navigation_observer.last_navigation_url());
457   RenderFrameHostImpl* portal_iframe =
458       portal_frame->child_at(0)->current_frame_host();
459   EXPECT_TRUE(static_cast<RenderWidgetHostViewBase*>(portal_iframe->GetView())
460                   ->IsRenderWidgetHostViewChildFrame());
461   RenderWidgetHostViewChildFrame* oopif_view =
462       static_cast<RenderWidgetHostViewChildFrame*>(portal_iframe->GetView());
463   EXPECT_NE(portal_frame->GetSiteInstance(), portal_iframe->GetSiteInstance());
464   WaitForHitTestData(portal_iframe);
465 
466   FailOnInputEvent no_input_to_portal_frame(
467       portal_frame->GetRenderWidgetHost());
468   FailOnInputEvent no_input_to_oopif(portal_iframe->GetRenderWidgetHost());
469   EXPECT_TRUE(ExecJs(main_frame,
470                      "var clicked = false;"
471                      "portal.onmousedown = _ => clicked = true;"));
472   EXPECT_TRUE(ExecJs(portal_frame,
473                      "var clicked = false;"
474                      "document.body.onmousedown = _ => clicked = true;"));
475   EXPECT_TRUE(ExecJs(portal_iframe,
476                      "var clicked = false;"
477                      "document.body.onmousedown = _ => clicked = true;"));
478 
479   // Route the mouse event.
480   gfx::Point root_location =
481       oopif_view->TransformPointToRootCoordSpace(gfx::Point(5, 5));
482   InputEventAckWaiter waiter(main_frame->GetRenderWidgetHost(),
483                              blink::WebInputEvent::kMouseDown);
484   SimulateRoutedMouseEvent(web_contents_impl, blink::WebInputEvent::kMouseDown,
485                            blink::WebPointerProperties::Button::kLeft,
486                            root_location);
487   waiter.Wait();
488 
489   // Check that the click event was only received by the main frame.
490   EXPECT_EQ(true, EvalJs(main_frame, "clicked"));
491   EXPECT_EQ(false, EvalJs(portal_frame, "clicked"));
492   EXPECT_EQ(false, EvalJs(portal_iframe, "clicked"));
493 }
494 
495 // Tests that an OOPIF inside a portal receives input events after the portal is
496 // activated.
497 // Flaky on macOS: https://crbug.com/1042703
498 #if defined(OS_MACOSX)
499 #define MAYBE_InputToOOPIFAfterActivation DISABLED_InputToOOPIFAfterActivation
500 #else
501 #define MAYBE_InputToOOPIFAfterActivation InputToOOPIFAfterActivation
502 #endif
IN_PROC_BROWSER_TEST_F(PortalHitTestBrowserTest,MAYBE_InputToOOPIFAfterActivation)503 IN_PROC_BROWSER_TEST_F(PortalHitTestBrowserTest,
504                        MAYBE_InputToOOPIFAfterActivation) {
505   EXPECT_TRUE(NavigateToURL(
506       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
507   WebContentsImpl* web_contents_impl =
508       static_cast<WebContentsImpl*>(shell()->web_contents());
509   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
510 
511   // Create portal.
512   // TODO(crbug.com/1029330): We currently need to give portal a large enough
513   // size to prevent overlap with iframe as this results in the test becoming
514   // flaky.
515   Portal* portal = nullptr;
516   {
517     PortalCreatedObserver portal_created_observer(main_frame);
518     GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
519     TestNavigationObserver navigation_observer(a_url);
520     navigation_observer.StartWatchingNewWebContents();
521     EXPECT_TRUE(ExecJs(
522         main_frame, JsReplace("var portal = document.createElement('portal');"
523                               "portal.src = $1;"
524                               "portal.style.width = '500px';"
525                               "portal.style.height = '500px';"
526                               "document.body.appendChild(portal);",
527                               a_url)));
528     portal = portal_created_observer.WaitUntilPortalCreated();
529     navigation_observer.Wait();
530   }
531   WebContentsImpl* portal_contents = portal->GetPortalContents();
532   RenderFrameHostImpl* portal_frame = portal_contents->GetMainFrame();
533   WaitForHitTestData(portal_frame);
534 
535   // Add an out-of-process iframe to the portal.
536   GURL b_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
537   TestNavigationObserver iframe_navigation_observer(portal_contents);
538   EXPECT_TRUE(ExecJs(portal_frame,
539                      JsReplace("var iframe = document.createElement('iframe');"
540                                "iframe.src = $1;"
541                                "document.body.appendChild(iframe);",
542                                b_url)));
543   iframe_navigation_observer.Wait();
544   EXPECT_EQ(b_url, iframe_navigation_observer.last_navigation_url());
545 
546   RenderFrameHostImpl* oopif = portal_frame->child_at(0)->current_frame_host();
547   RenderWidgetHostViewBase* oopif_view =
548       static_cast<RenderWidgetHostViewBase*>(oopif->GetView());
549   EXPECT_TRUE(oopif_view->IsRenderWidgetHostViewChildFrame());
550   EXPECT_NE(portal_frame->GetSiteInstance(), oopif->GetSiteInstance());
551   WaitForHitTestData(oopif);
552   EXPECT_TRUE(ExecJs(oopif,
553                      "var clicked = false;"
554                      "document.body.onmousedown = _ => clicked = true;"));
555 
556   // Activate the portal.
557   {
558     PortalActivatedObserver activated_observer(portal);
559     EXPECT_TRUE(ExecJs(main_frame,
560                        "let portal = document.querySelector('portal');"
561                        "portal.activate().then(() => { "
562                        "  document.body.removeChild(portal); "
563                        "});"));
564     activated_observer.WaitForActivate();
565 
566     RenderWidgetHostViewBase* view =
567         portal_frame->GetRenderWidgetHost()->GetView();
568     viz::FrameSinkId root_frame_sink_id = view->GetRootFrameSinkId();
569     HitTestRegionObserver hit_test_observer(root_frame_sink_id);
570 
571     // The hit test region for the portal frame should be at index 1 after
572     // activation, so we wait for the hit test data to update until it's in
573     // this state.
574     auto hit_test_index = [&]() -> base::Optional<size_t> {
575       const auto& display_hit_test_query_map =
576           GetHostFrameSinkManager()->display_hit_test_query();
577       auto it = display_hit_test_query_map.find(root_frame_sink_id);
578       // On Mac, we create a new root layer after activation, so the hit test
579       // data may not have anything for the new layer yet.
580       if (it == display_hit_test_query_map.end())
581         return base::nullopt;
582       CHECK_EQ(portal_frame->GetRenderWidgetHost()->GetView(), view);
583       size_t index;
584       if (!it->second->FindIndexOfFrameSink(view->GetFrameSinkId(), &index))
585         return base::nullopt;
586       return index;
587     };
588     hit_test_observer.WaitForHitTestData();
589     while (hit_test_index() != 1u)
590       hit_test_observer.WaitForHitTestDataChange();
591   }
592   EXPECT_EQ(shell()->web_contents(), portal_contents);
593 
594   // Send a mouse event to the OOPIF.
595   gfx::Point root_location =
596       oopif_view->TransformPointToRootCoordSpace(gfx::Point(10, 10));
597   InputEventAckWaiter waiter(oopif->GetRenderWidgetHost(),
598                              blink::WebInputEvent::kMouseDown);
599   SimulateRoutedMouseEvent(
600       shell()->web_contents(), blink::WebInputEvent::kMouseDown,
601       blink::WebPointerProperties::Button::kLeft, root_location);
602   waiter.Wait();
603 
604   // Check that the click event was received by the iframe.
605   EXPECT_EQ(true, EvalJs(oopif, "clicked"));
606 }
607 
608 // Tests that async hit testing does not target portals.
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,AsyncEventTargetingIgnoresPortals)609 IN_PROC_BROWSER_TEST_F(PortalBrowserTest, AsyncEventTargetingIgnoresPortals) {
610   EXPECT_TRUE(NavigateToURL(
611       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
612   WebContentsImpl* web_contents_impl =
613       static_cast<WebContentsImpl*>(shell()->web_contents());
614   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
615 
616   GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
617   Portal* portal = CreatePortalToUrl(web_contents_impl, a_url);
618   WebContentsImpl* portal_contents = portal->GetPortalContents();
619   RenderFrameHostImpl* portal_frame = portal_contents->GetMainFrame();
620   ASSERT_TRUE(static_cast<RenderWidgetHostViewBase*>(portal_frame->GetView())
621                   ->IsRenderWidgetHostViewChildFrame());
622   RenderWidgetHostViewChildFrame* portal_view =
623       static_cast<RenderWidgetHostViewChildFrame*>(portal_frame->GetView());
624   WaitForHitTestData(portal_frame);
625 
626   viz::mojom::InputTargetClient* target_client =
627       main_frame->GetRenderWidgetHost()->input_target_client();
628   ASSERT_TRUE(target_client);
629 
630   gfx::PointF root_location =
631       portal_view->TransformPointToRootCoordSpaceF(gfx::PointF(5, 5));
632 
633   // Query the renderer for the target widget. The root should claim the point
634   // for itself, not the portal.
635   base::RunLoop run_loop;
636   base::OnceClosure quit_closure = run_loop.QuitClosure();
637   viz::FrameSinkId received_frame_sink_id;
638   target_client->FrameSinkIdAt(
639       root_location, 0,
640       base::BindLambdaForTesting(
641           [&](const viz::FrameSinkId& id, const gfx::PointF& point) {
642             received_frame_sink_id = id;
643             std::move(quit_closure).Run();
644           }));
645   run_loop.Run();
646 
647   viz::FrameSinkId root_frame_sink_id =
648       static_cast<RenderWidgetHostViewBase*>(main_frame->GetView())
649           ->GetFrameSinkId();
650   EXPECT_EQ(root_frame_sink_id, received_frame_sink_id)
651       << "Note: The portal's FrameSinkId is " << portal_view->GetFrameSinkId();
652 }
653 
654 // Tests that trying to navigate to a chrome:// URL kills the renderer.
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,NavigateToChrome)655 IN_PROC_BROWSER_TEST_F(PortalBrowserTest, NavigateToChrome) {
656   EXPECT_TRUE(NavigateToURL(
657       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
658   WebContentsImpl* web_contents_impl =
659       static_cast<WebContentsImpl*>(shell()->web_contents());
660   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
661 
662   // Create portal.
663   PortalCreatedObserver portal_created_observer(main_frame);
664   EXPECT_TRUE(ExecJs(main_frame,
665                      "var portal = document.createElement('portal');"
666                      "document.body.appendChild(portal);"));
667   Portal* portal = portal_created_observer.WaitUntilPortalCreated();
668   PortalInterceptorForTesting* portal_interceptor =
669       PortalInterceptorForTesting::From(portal);
670 
671   // Try to navigate to chrome://settings and wait for the process to die.
672   portal_interceptor->SetNavigateCallback(base::BindRepeating(
673       [](Portal* portal, const GURL& url, blink::mojom::ReferrerPtr referrer,
674          blink::mojom::Portal::NavigateCallback callback) {
675         GURL chrome_url("chrome://settings");
676         portal->Navigate(chrome_url, std::move(referrer), std::move(callback));
677       },
678       portal));
679   RenderProcessHostBadIpcMessageWaiter kill_waiter(main_frame->GetProcess());
680   GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
681   ignore_result(ExecJs(main_frame, JsReplace("portal.src = $1;", a_url)));
682 
683   EXPECT_EQ(bad_message::RPH_MOJO_PROCESS_ERROR, kill_waiter.Wait());
684 }
685 
686 // Regression test for crbug.com/969714. Tests that receiving a touch ack
687 // from the predecessor after portal activation doesn't cause a crash.
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,TouchAckAfterActivate)688 IN_PROC_BROWSER_TEST_F(PortalBrowserTest, TouchAckAfterActivate) {
689   EXPECT_TRUE(NavigateToURL(
690       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
691   WebContentsImpl* web_contents_impl =
692       static_cast<WebContentsImpl*>(shell()->web_contents());
693   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
694 
695   GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
696   Portal* portal = CreatePortalToUrl(web_contents_impl, a_url);
697   EXPECT_TRUE(
698       ExecJs(main_frame,
699              JsReplace("document.body.addEventListener('touchstart', e => {"
700                        "  document.querySelector('portal').activate();"
701                        "}, {passive: false});")));
702   WebContentsImpl* portal_contents = portal->GetPortalContents();
703 
704   RenderWidgetHostImpl* render_widget_host = main_frame->GetRenderWidgetHost();
705   RenderFrameHostImpl* portal_frame = portal_contents->GetMainFrame();
706   RenderWidgetHostViewChildFrame* portal_view =
707       static_cast<RenderWidgetHostViewChildFrame*>(portal_frame->GetView());
708   InputEventAckWaiter input_event_ack_waiter(
709       render_widget_host, blink::WebInputEvent::Type::kTouchStart);
710   WaitForHitTestData(portal_frame);
711 
712   SyntheticTapGestureParams params;
713   params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
714   params.position =
715       portal_view->TransformPointToRootCoordSpaceF(gfx::PointF(5, 5));
716 
717   PortalActivatedObserver activated_observer(portal);
718   std::unique_ptr<SyntheticTapGesture> gesture =
719       std::make_unique<SyntheticTapGesture>(params);
720   render_widget_host->QueueSyntheticGesture(std::move(gesture),
721                                             base::DoNothing());
722   activated_observer.WaitForActivate();
723   EXPECT_EQ(portal_contents, shell()->web_contents());
724 
725   // Wait for a touch ack to be sent from the predecessor.
726   input_event_ack_waiter.Wait();
727 }
728 
729 // Regression test for crbug.com/973647. Tests that receiving a touch ack
730 // after activation and predecessor adoption doesn't cause a crash.
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,TouchAckAfterActivateAndAdopt)731 IN_PROC_BROWSER_TEST_F(PortalBrowserTest, TouchAckAfterActivateAndAdopt) {
732   EXPECT_TRUE(NavigateToURL(
733       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
734   WebContentsImpl* web_contents_impl =
735       static_cast<WebContentsImpl*>(shell()->web_contents());
736   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
737 
738   GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
739   Portal* portal = CreatePortalToUrl(web_contents_impl, a_url);
740   const int TOUCH_ACK_DELAY_IN_MILLISECONDS = 500;
741   EXPECT_TRUE(
742       ExecJs(main_frame,
743              JsReplace("document.body.addEventListener('touchstart', e => {"
744                        "  document.querySelector('portal').activate();"
745                        "  var stop = performance.now() + $1;"
746                        "  while (performance.now() < stop) {}"
747                        "}, {passive: false});",
748                        TOUCH_ACK_DELAY_IN_MILLISECONDS)));
749   WebContentsImpl* portal_contents = portal->GetPortalContents();
750 
751   RenderFrameHostImpl* portal_frame = portal_contents->GetMainFrame();
752   EXPECT_TRUE(ExecJs(portal_frame,
753                      "window.addEventListener('portalactivate', e => {"
754                      "  var portal = e.adoptPredecessor();"
755                      "  document.body.appendChild(portal);"
756                      "});"));
757   WaitForHitTestData(portal_frame);
758 
759   RenderWidgetHostImpl* render_widget_host = main_frame->GetRenderWidgetHost();
760   InputEventAckWaiter input_event_ack_waiter(
761       render_widget_host, blink::WebInputEvent::Type::kTouchStart);
762 
763   SyntheticTapGestureParams params;
764   RenderWidgetHostViewChildFrame* portal_view =
765       static_cast<RenderWidgetHostViewChildFrame*>(portal_frame->GetView());
766   params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
767   params.position =
768       portal_view->TransformPointToRootCoordSpaceF(gfx::PointF(5, 5));
769 
770   std::unique_ptr<SyntheticTapGesture> gesture =
771       std::make_unique<SyntheticTapGesture>(params);
772   {
773     PortalActivatedObserver activated_observer(portal);
774     PortalCreatedObserver adoption_observer(portal_frame);
775     render_widget_host->QueueSyntheticGesture(std::move(gesture),
776                                               base::DoNothing());
777     activated_observer.WaitForActivate();
778     EXPECT_EQ(portal_contents, shell()->web_contents());
779     // Wait for predecessor to be adopted.
780     adoption_observer.WaitUntilPortalCreated();
781   }
782 
783   // Wait for a touch ack to be sent from the predecessor.
784   input_event_ack_waiter.Wait();
785 }
786 
787 // Regression test for crbug.com/973647. Tests that receiving a touch ack
788 // after activation and reactivating a predecessor doesn't cause a crash.
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,TouchAckAfterActivateAndReactivate)789 IN_PROC_BROWSER_TEST_F(PortalBrowserTest, TouchAckAfterActivateAndReactivate) {
790   EXPECT_TRUE(NavigateToURL(
791       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
792   WebContentsImpl* web_contents_impl =
793       static_cast<WebContentsImpl*>(shell()->web_contents());
794   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
795 
796   GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
797   Portal* portal = CreatePortalToUrl(web_contents_impl, a_url);
798   const int TOUCH_ACK_DELAY_IN_MILLISECONDS = 500;
799   EXPECT_TRUE(
800       ExecJs(main_frame,
801              JsReplace("document.body.addEventListener('touchstart', e => {"
802                        "  document.querySelector('portal').activate();"
803                        "  var stop = performance.now() + $1;"
804                        "  while (performance.now() < stop) {}"
805                        "}, {passive: false});",
806                        TOUCH_ACK_DELAY_IN_MILLISECONDS)));
807   WebContentsImpl* portal_contents = portal->GetPortalContents();
808 
809   RenderFrameHostImpl* portal_frame = portal_contents->GetMainFrame();
810   EXPECT_TRUE(ExecJs(portal_frame,
811                      "window.addEventListener('portalactivate', e => {"
812                      "  var portal = e.adoptPredecessor();"
813                      "  document.body.appendChild(portal);"
814                      "  portal.activate();"
815                      "});"));
816   WaitForHitTestData(portal_frame);
817 
818   RenderWidgetHostImpl* render_widget_host = main_frame->GetRenderWidgetHost();
819   InputEventAckWaiter input_event_ack_waiter(
820       render_widget_host, blink::WebInputEvent::Type::kTouchStart);
821 
822   SyntheticTapGestureParams params;
823   params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
824   params.position = gfx::PointF(20, 20);
825 
826   std::unique_ptr<SyntheticTapGesture> gesture =
827       std::make_unique<SyntheticTapGesture>(params);
828 
829   base::Optional<PortalActivatedObserver> predecessor_activated;
830   {
831     PortalCreatedObserver adoption_observer(portal_frame);
832     adoption_observer.set_created_callback(base::BindLambdaForTesting(
833         [&](Portal* portal) { predecessor_activated.emplace(portal); }));
834 
835     PortalActivatedObserver activated_observer(portal);
836     render_widget_host->QueueSyntheticGesture(std::move(gesture),
837                                               base::DoNothing());
838     activated_observer.WaitForActivate();
839     EXPECT_EQ(portal_contents, shell()->web_contents());
840 
841     adoption_observer.WaitUntilPortalCreated();
842   }
843 
844   predecessor_activated->WaitForActivate();
845   // Sanity check to see if the predecessor was reactivated.
846   EXPECT_EQ(web_contents_impl, shell()->web_contents());
847 
848   // Wait for a touch ack to be sent from the predecessor.
849   input_event_ack_waiter.Wait();
850 }
851 
852 // TODO(crbug.com/985078): Fix on Mac.
853 #if !defined(OS_MACOSX)
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,TouchStateClearedBeforeActivation)854 IN_PROC_BROWSER_TEST_F(PortalBrowserTest, TouchStateClearedBeforeActivation) {
855   EXPECT_TRUE(NavigateToURL(
856       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
857   WebContentsImpl* web_contents_impl =
858       static_cast<WebContentsImpl*>(shell()->web_contents());
859   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
860 
861   GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
862   Portal* portal = CreatePortalToUrl(web_contents_impl, a_url);
863   EXPECT_TRUE(
864       ExecJs(main_frame,
865              JsReplace("document.body.addEventListener('touchstart', e => {"
866                        "  document.querySelector('portal').activate();"
867                        "}, {passive: false});")));
868   WebContentsImpl* portal_contents = portal->GetPortalContents();
869 
870   RenderFrameHostImpl* portal_frame = portal_contents->GetMainFrame();
871   EXPECT_TRUE(ExecJs(portal_frame,
872                      "window.addEventListener('portalactivate', e => {"
873                      "  var portal = e.adoptPredecessor();"
874                      "  document.body.appendChild(portal);"
875                      "  portal.activate();"
876                      "});"));
877   WaitForHitTestData(portal_frame);
878 
879   RenderWidgetHostImpl* render_widget_host = main_frame->GetRenderWidgetHost();
880 
881   SyntheticTapGestureParams params;
882   params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
883   params.position = gfx::PointF(20, 20);
884 
885   // Activate the portal, and then wait for the predecessor to be reactivated.
886   base::Optional<PortalActivatedObserver> adopted_activated;
887   {
888     PortalCreatedObserver adoption_observer(portal_frame);
889     adoption_observer.set_created_callback(base::BindLambdaForTesting(
890         [&](Portal* portal) { adopted_activated.emplace(portal); }));
891 
892     PortalActivatedObserver activated_observer(portal);
893     std::unique_ptr<SyntheticTapGesture> gesture =
894         std::make_unique<SyntheticTapGesture>(params);
895     InputEventAckWaiter input_event_ack_waiter(
896         render_widget_host, blink::WebInputEvent::Type::kTouchCancel);
897     render_widget_host->QueueSyntheticGestureCompleteImmediately(
898         std::move(gesture));
899     // Wait for synthetic cancel event to be sent.
900     input_event_ack_waiter.Wait();
901     activated_observer.WaitForActivate();
902     EXPECT_EQ(portal_contents, shell()->web_contents());
903 
904     adoption_observer.WaitUntilPortalCreated();
905   }
906   adopted_activated->WaitForActivate();
907   // Sanity check to see if the predecessor was reactivated.
908   EXPECT_EQ(web_contents_impl, shell()->web_contents());
909 
910   InputEventAckWaiter input_event_ack_waiter(
911       render_widget_host, blink::WebInputEvent::Type::kTouchStart);
912   std::unique_ptr<SyntheticTapGesture> gesture =
913       std::make_unique<SyntheticTapGesture>(params);
914   render_widget_host->QueueSyntheticGesture(std::move(gesture),
915                                             base::DoNothing());
916   // Waits for touch to be acked. If touch state wasn't cleared before initial
917   // activation, a DCHECK will be hit before the ack is sent.
918   input_event_ack_waiter.Wait();
919 }
920 #endif
921 
922 // TODO(crbug.com/985078): Fix on Mac.
923 #if !defined(OS_MACOSX)
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,GestureCleanedUpBeforeActivation)924 IN_PROC_BROWSER_TEST_F(PortalBrowserTest, GestureCleanedUpBeforeActivation) {
925   EXPECT_TRUE(NavigateToURL(
926       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
927   WebContentsImpl* web_contents_impl =
928       static_cast<WebContentsImpl*>(shell()->web_contents());
929   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
930 
931   GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
932   Portal* portal = CreatePortalToUrl(web_contents_impl, a_url);
933   EXPECT_TRUE(
934       ExecJs(main_frame,
935              JsReplace("document.body.addEventListener('touchstart', e => {"
936                        "  document.querySelector('portal').activate();"
937                        "}, {once: true});")));
938   WebContentsImpl* portal_contents = portal->GetPortalContents();
939   RenderFrameHostImpl* portal_frame = portal_contents->GetMainFrame();
940   EXPECT_TRUE(ExecJs(portal_frame,
941                      "window.addEventListener('portalactivate', e => {"
942                      "  var portal = e.adoptPredecessor();"
943                      "  document.body.appendChild(portal);"
944                      "  portal.activate(); "
945                      "});"));
946   WaitForHitTestData(portal_frame);
947 
948   RenderWidgetHostImpl* render_widget_host = main_frame->GetRenderWidgetHost();
949 
950   SyntheticTapGestureParams params;
951   params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
952   params.position = gfx::PointF(20, 20);
953   params.duration_ms = 1;
954 
955   // Simulate a tap and activate the portal.
956   base::Optional<PortalActivatedObserver> adopted_activated;
957   {
958     PortalCreatedObserver adoption_observer(portal_frame);
959     adoption_observer.set_created_callback(base::BindLambdaForTesting(
960         [&](Portal* portal) { adopted_activated.emplace(portal); }));
961 
962     PortalActivatedObserver activated_observer(portal);
963     std::unique_ptr<SyntheticTapGesture> gesture =
964         std::make_unique<SyntheticTapGesture>(params);
965     render_widget_host->QueueSyntheticGestureCompleteImmediately(
966         std::move(gesture));
967     activated_observer.WaitForActivate();
968     EXPECT_EQ(portal_contents, shell()->web_contents());
969 
970     adoption_observer.WaitUntilPortalCreated();
971   }
972 
973   // Wait for predecessor to be reactivated.
974   adopted_activated->WaitForActivate();
975   EXPECT_EQ(web_contents_impl, shell()->web_contents());
976 
977   // Simulate another tap.
978   InputEventAckWaiter input_event_ack_waiter(
979       render_widget_host, blink::WebInputEvent::Type::kGestureTap);
980   auto gesture = std::make_unique<SyntheticTapGesture>(params);
981   render_widget_host->QueueSyntheticGesture(std::move(gesture),
982                                             base::DoNothing());
983   // Wait for the tap gesture ack. If the initial gesture wasn't cleaned up, the
984   // new gesture created will cause an error in the gesture validator.
985   input_event_ack_waiter.Wait();
986 }
987 #endif
988 
989 // Touch input transfer is only implemented in the content layer for Aura.
990 #if defined(USE_AURA)
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,TouchInputTransferAcrossActivation)991 IN_PROC_BROWSER_TEST_F(PortalBrowserTest, TouchInputTransferAcrossActivation) {
992   EXPECT_TRUE(NavigateToURL(
993       shell(),
994       embedded_test_server()->GetURL("portal.test", "/portals/scroll.html")));
995   WebContentsImpl* web_contents_impl =
996       static_cast<WebContentsImpl*>(shell()->web_contents());
997   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
998   RenderWidgetHostImpl* render_widget_host = main_frame->GetRenderWidgetHost();
999 
1000   GURL portal_url(embedded_test_server()->GetURL(
1001       "portal.test", "/portals/scroll-portal.html"));
1002   Portal* portal = CreatePortalToUrl(web_contents_impl, portal_url);
1003   WebContentsImpl* portal_contents = portal->GetPortalContents();
1004   RenderFrameHostImpl* portal_frame = portal_contents->GetMainFrame();
1005   WaitForHitTestData(portal_frame);
1006 
1007   // Create and dispatch a synthetic scroll to trigger activation.
1008   SyntheticSmoothScrollGestureParams params;
1009   params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
1010   params.anchor =
1011       gfx::PointF(50, main_frame->GetView()->GetViewBounds().height() - 100);
1012   params.distances.push_back(-gfx::Vector2d(0, 100));
1013 
1014   std::unique_ptr<SyntheticSmoothScrollGesture> gesture =
1015       std::make_unique<SyntheticSmoothScrollGesture>(params);
1016   base::RunLoop run_loop;
1017   render_widget_host->QueueSyntheticGesture(
1018       std::move(gesture),
1019       base::BindLambdaForTesting([&](SyntheticGesture::Result result) {
1020         EXPECT_EQ(SyntheticGesture::Result::GESTURE_FINISHED, result);
1021         run_loop.Quit();
1022       }));
1023   run_loop.Run();
1024 
1025   // Check if the activated page scrolled.
1026   EXPECT_NE(0, EvalJs(portal_frame, "window.scrollY"));
1027 }
1028 #endif
1029 
1030 // TODO(crbug.com/1010675): Test fails flakily.
1031 // Touch input transfer is only implemented in the content layer for Aura.
1032 #if defined(USE_AURA)
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,TouchInputTransferAcrossReactivation)1033 IN_PROC_BROWSER_TEST_F(PortalBrowserTest,
1034                        TouchInputTransferAcrossReactivation) {
1035   EXPECT_TRUE(NavigateToURL(
1036       shell(), embedded_test_server()->GetURL(
1037                    "portal.test",
1038                    "/portals/touch-input-transfer-across-reactivation.html")));
1039   WebContentsImpl* web_contents_impl =
1040       static_cast<WebContentsImpl*>(shell()->web_contents());
1041   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
1042   RenderWidgetHostImpl* render_widget_host = main_frame->GetRenderWidgetHost();
1043 
1044   GURL portal_url(embedded_test_server()->GetURL(
1045       "portal.test", "/portals/reactivate-predecessor.html"));
1046   Portal* portal = CreatePortalToUrl(web_contents_impl, portal_url);
1047   WaitForHitTestData(main_frame);
1048 
1049   PortalActivatedObserver activated_observer(portal);
1050 
1051   // Create and dispatch a synthetic scroll to trigger activation.
1052   SyntheticSmoothScrollGestureParams params;
1053   params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
1054   params.anchor =
1055       gfx::PointF(50, main_frame->GetView()->GetViewBounds().height() - 100);
1056   params.distances.push_back(-gfx::Vector2d(0, 250));
1057   params.speed_in_pixels_s = 200;
1058 
1059   std::unique_ptr<SyntheticSmoothScrollGesture> gesture =
1060       std::make_unique<SyntheticSmoothScrollGesture>(params);
1061   base::RunLoop run_loop;
1062   render_widget_host->QueueSyntheticGesture(
1063       std::move(gesture),
1064       base::BindLambdaForTesting([&](SyntheticGesture::Result result) {
1065         EXPECT_EQ(SyntheticGesture::Result::GESTURE_FINISHED, result);
1066         run_loop.Quit();
1067       }));
1068   // Portal should activate when the gesture begins.
1069   activated_observer.WaitForActivate();
1070   // Wait till the scroll gesture finishes.
1071   run_loop.Run();
1072   // The predecessor should have been reactivated (we should be back to the
1073   // starting page).
1074   EXPECT_EQ(web_contents_impl, shell()->web_contents());
1075   // The starting page should have scrolled.
1076   // NOTE: This assumes that the scroll gesture is long enough that touch events
1077   // are still sent after the predecessor is reactivated.
1078   int scroll_y_after_portal_activate =
1079       EvalJs(main_frame, "scrollYAfterPortalActivate").ExtractInt();
1080   EXPECT_LT(scroll_y_after_portal_activate,
1081             EvalJs(main_frame, "window.scrollY"));
1082 }
1083 #endif
1084 
1085 // Tests that the outer FrameTreeNode is deleted after activation.
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,FrameDeletedAfterActivation)1086 IN_PROC_BROWSER_TEST_F(PortalBrowserTest, FrameDeletedAfterActivation) {
1087   EXPECT_TRUE(NavigateToURL(
1088       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
1089   WebContentsImpl* web_contents_impl =
1090       static_cast<WebContentsImpl*>(shell()->web_contents());
1091   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
1092 
1093   GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
1094   Portal* portal = CreatePortalToUrl(web_contents_impl, a_url);
1095   WebContentsImpl* portal_contents = portal->GetPortalContents();
1096 
1097   FrameTreeNode* outer_frame_tree_node = FrameTreeNode::GloballyFindByID(
1098       portal_contents->GetOuterDelegateFrameTreeNodeId());
1099   EXPECT_TRUE(outer_frame_tree_node);
1100 
1101   EXPECT_TRUE(ExecJs(portal_contents->GetMainFrame(),
1102                      "window.onportalactivate = e => "
1103                      "document.body.appendChild(e.adoptPredecessor());"));
1104 
1105   {
1106     FrameDeletedObserver observer(outer_frame_tree_node->current_frame_host());
1107     PortalCreatedObserver portal_created_observer(
1108         portal_contents->GetMainFrame());
1109     ExecuteScriptAsync(main_frame,
1110                        "document.querySelector('portal').activate();");
1111     observer.Wait();
1112 
1113     // Observes the creation of a new portal due to the adoption of the
1114     // predecessor during the activate event.
1115     // TODO(lfg): We only wait for the adoption callback to avoid a race
1116     // receiving a sync IPC in a nested message loop while the browser is
1117     // sending out another sync IPC to the GPU process.
1118     // https://crbug.com/976367.
1119     portal_created_observer.WaitUntilPortalCreated();
1120   }
1121 }
1122 
1123 // Tests that activating a portal at the same time as it is being removed
1124 // doesn't crash the browser.
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,RemovePortalWhenUnloading)1125 IN_PROC_BROWSER_TEST_F(PortalBrowserTest, RemovePortalWhenUnloading) {
1126   EXPECT_TRUE(NavigateToURL(
1127       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
1128   WebContentsImpl* web_contents_impl =
1129       static_cast<WebContentsImpl*>(shell()->web_contents());
1130   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
1131 
1132   // Create a container for the portal.
1133   EXPECT_TRUE(ExecJs(main_frame,
1134                      "var div = document.createElement('div');"
1135                      "document.body.appendChild(div);"));
1136 
1137   // Create portal.
1138   PortalCreatedObserver portal_created_observer(main_frame);
1139   EXPECT_TRUE(ExecJs(main_frame,
1140                      "var portal = document.createElement('portal');"
1141                      "div.appendChild(portal);"));
1142 
1143   // Add a same-origin iframe in the same div as the portal that activates the
1144   // portal on its unload handler.
1145   EXPECT_TRUE(
1146       ExecJs(main_frame,
1147              "var iframe = document.createElement('iframe');"
1148              "iframe.src = 'about:blank';"
1149              "div.appendChild(iframe);"
1150              "iframe.contentWindow.onunload = () => portal.activate();"));
1151 
1152   // Remove the div from the document. This destroys the portal's WebContents
1153   // and should destroy the Portal object as well, so that the activate message
1154   // is not processed.
1155   EXPECT_TRUE(ExecJs(main_frame, "div.remove();"));
1156 }
1157 
1158 // Tests that a portal can navigate while orphaned.
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,OrphanedNavigation)1159 IN_PROC_BROWSER_TEST_F(PortalBrowserTest, OrphanedNavigation) {
1160   EXPECT_TRUE(NavigateToURL(
1161       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
1162   WebContentsImpl* web_contents_impl =
1163       static_cast<WebContentsImpl*>(shell()->web_contents());
1164   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
1165 
1166   GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
1167   Portal* portal = CreatePortalToUrl(web_contents_impl, a_url);
1168   WebContentsImpl* portal_contents = portal->GetPortalContents();
1169 
1170   // Block the activate callback so that the predecessor portal stays orphaned.
1171   EXPECT_TRUE(ExecJs(portal_contents->GetMainFrame(),
1172                      "window.onportalactivate = e => { while(true) {} };"));
1173 
1174   // Activate the portal and navigate the predecessor.
1175   PortalActivatedObserver activated_observer(portal);
1176   TestNavigationObserver main_frame_navigation_observer(web_contents_impl);
1177   ExecuteScriptAsync(main_frame,
1178                      "document.querySelector('portal').activate();"
1179                      "window.location.reload()");
1180   activated_observer.WaitForActivate();
1181   main_frame_navigation_observer.Wait();
1182 }
1183 
1184 // Tests that the browser doesn't crash if the renderer tries to create the
1185 // PortalHost after the parent renderer dropped the portal.
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,AccessPortalHostAfterPortalDestruction)1186 IN_PROC_BROWSER_TEST_F(PortalBrowserTest,
1187                        AccessPortalHostAfterPortalDestruction) {
1188   EXPECT_TRUE(NavigateToURL(
1189       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
1190   WebContentsImpl* web_contents_impl =
1191       static_cast<WebContentsImpl*>(shell()->web_contents());
1192 
1193   GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
1194   Portal* portal = CreatePortalToUrl(web_contents_impl, a_url);
1195   WebContentsImpl* portal_contents = portal->GetPortalContents();
1196   RenderFrameHostImpl* portal_frame = portal_contents->GetMainFrame();
1197 
1198   // Simulate the portal being dropped, but not the destruction of the
1199   // WebContents.
1200   web_contents_impl->GetMainFrame()->DestroyPortal(portal);
1201 
1202   // Get the portal renderer to access the WebContents.
1203   RenderProcessHostBadIpcMessageWaiter kill_waiter(portal_frame->GetProcess());
1204   ExecuteScriptAsync(portal_frame,
1205                      "window.portalHost.postMessage('message', '*');");
1206   EXPECT_EQ(bad_message::RPH_MOJO_PROCESS_ERROR, kill_waiter.Wait());
1207 }
1208 
1209 // Tests that activation early in navigation succeeds, cancelling the pending
1210 // navigation.
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,ActivateEarlyInNavigation)1211 IN_PROC_BROWSER_TEST_F(PortalBrowserTest, ActivateEarlyInNavigation) {
1212   EXPECT_TRUE(NavigateToURL(
1213       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
1214   WebContentsImpl* web_contents_impl =
1215       static_cast<WebContentsImpl*>(shell()->web_contents());
1216   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
1217 
1218   GURL url = embedded_test_server()->GetURL("a.com", "/title2.html");
1219   Portal* portal = CreatePortalToUrl(web_contents_impl, url);
1220   WebContents* portal_contents = portal->GetPortalContents();
1221 
1222   // Have the outer page try to navigate away but stop it early in the request,
1223   // where it is still possible to stop.
1224   GURL destination =
1225       embedded_test_server()->GetURL("portal.test", "/title3.html");
1226   TestNavigationObserver navigation_observer(web_contents_impl);
1227   NavigationHandleObserver handle_observer(web_contents_impl, destination);
1228   TestNavigationManager navigation_manager(web_contents_impl, destination);
1229   NavigationController::LoadURLParams params(destination);
1230   params.transition_type = ui::PageTransitionFromInt(
1231       ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
1232   web_contents_impl->GetController().LoadURLWithParams(params);
1233   ASSERT_TRUE(navigation_manager.WaitForRequestStart());
1234 
1235   // Then activate the portal. Since this is early in navigation, it should be
1236   // aborted and the portal activation should succeed.
1237   PortalActivatedObserver activated_observer(portal);
1238   ExecuteScriptAsync(main_frame,
1239                      "document.querySelector('portal').activate();");
1240   EXPECT_EQ(blink::mojom::PortalActivateResult::kPredecessorWillUnload,
1241             activated_observer.WaitForActivateResult());
1242   EXPECT_EQ(portal_contents, shell()->web_contents());
1243   navigation_observer.Wait();
1244   EXPECT_EQ(handle_observer.net_error_code(), net::ERR_ABORTED);
1245 }
1246 
1247 // Tests that activation late in navigation is rejected (since it's too late to
1248 // stop the navigation).
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,ActivateLateInNavigation)1249 IN_PROC_BROWSER_TEST_F(PortalBrowserTest, ActivateLateInNavigation) {
1250   EXPECT_TRUE(NavigateToURL(
1251       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
1252   WebContentsImpl* web_contents_impl =
1253       static_cast<WebContentsImpl*>(shell()->web_contents());
1254   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
1255 
1256   GURL url = embedded_test_server()->GetURL("a.com", "/title2.html");
1257   Portal* portal = CreatePortalToUrl(web_contents_impl, url);
1258 
1259   // Have the outer page try to navigate away and reach the point where it's
1260   // about to process the response (after which it will commit). It is too late
1261   // to abort the navigation.
1262   GURL destination =
1263       embedded_test_server()->GetURL("portal.test", "/title3.html");
1264   TestNavigationObserver navigation_observer(web_contents_impl);
1265   TestNavigationManager navigation_manager(web_contents_impl, destination);
1266   NavigationController::LoadURLParams params(destination);
1267   params.transition_type = ui::PageTransitionFromInt(
1268       ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
1269   web_contents_impl->GetController().LoadURLWithParams(params);
1270   ASSERT_TRUE(navigation_manager.WaitForResponse());
1271 
1272   // Then activate the portal. Since this is late in navigation, we expect the
1273   // activation to fail. Since commit hasn't actually happened yet, though,
1274   // there is time for the renderer to process the promise rejection.
1275   PortalActivatedObserver activated_observer(portal);
1276   EvalJsResult result = EvalJs(main_frame,
1277                                "document.querySelector('portal').activate()"
1278                                ".then(() => 'success', e => e.message)");
1279   EXPECT_THAT(result.ExtractString(),
1280               ::testing::HasSubstr("navigation is in progress"));
1281   EXPECT_EQ(
1282       blink::mojom::PortalActivateResult::kRejectedDueToPredecessorNavigation,
1283       activated_observer.result());
1284 
1285   // The navigation should commit properly thereafter.
1286   navigation_manager.ResumeNavigation();
1287   navigation_observer.Wait();
1288   EXPECT_EQ(web_contents_impl, shell()->web_contents());
1289   EXPECT_TRUE(navigation_observer.last_navigation_succeeded());
1290   EXPECT_EQ(destination, navigation_observer.last_navigation_url());
1291 }
1292 
1293 namespace {
1294 
1295 class NavigationControlInterceptorBadPortalActivateResult
1296     : public mojom::FrameNavigationControlInterceptorForTesting {
1297  public:
NavigationControlInterceptorBadPortalActivateResult(RenderFrameHostImpl * frame_host)1298   explicit NavigationControlInterceptorBadPortalActivateResult(
1299       RenderFrameHostImpl* frame_host)
1300       : frame_host_(frame_host) {}
1301 
GetForwardingInterface()1302   mojom::FrameNavigationControl* GetForwardingInterface() override {
1303     if (!navigation_control_)
1304       frame_host_->GetRemoteAssociatedInterfaces()->GetInterface(
1305           &navigation_control_);
1306     return navigation_control_.get();
1307   }
1308 
OnPortalActivated(const base::UnguessableToken & portal_token,mojo::PendingAssociatedRemote<blink::mojom::Portal> portal,mojo::PendingAssociatedReceiver<blink::mojom::PortalClient> portal_client,blink::TransferableMessage data,OnPortalActivatedCallback callback)1309   void OnPortalActivated(
1310       const base::UnguessableToken& portal_token,
1311       mojo::PendingAssociatedRemote<blink::mojom::Portal> portal,
1312       mojo::PendingAssociatedReceiver<blink::mojom::PortalClient> portal_client,
1313       blink::TransferableMessage data,
1314       OnPortalActivatedCallback callback) override {
1315     GetForwardingInterface()->OnPortalActivated(
1316         portal_token, std::move(portal), std::move(portal_client),
1317         std::move(data),
1318         base::BindOnce(
1319             [](OnPortalActivatedCallback callback,
1320                blink::mojom::PortalActivateResult) {
1321               // Replace the true result with one that the renderer is not
1322               // allowed to send.
1323               std::move(callback).Run(blink::mojom::PortalActivateResult::
1324                                           kRejectedDueToPredecessorNavigation);
1325             },
1326             std::move(callback)));
1327   }
1328 
1329  private:
1330   RenderFrameHostImpl* frame_host_;
1331   mojo::AssociatedRemote<mojom::FrameNavigationControl> navigation_control_;
1332 };
1333 
1334 class RenderFrameHostImplForNavigationControlInterceptor
1335     : public RenderFrameHostImpl {
1336  private:
1337   using RenderFrameHostImpl::RenderFrameHostImpl;
1338 
GetNavigationControl()1339   mojom::FrameNavigationControl* GetNavigationControl() override {
1340     return &interceptor_;
1341   }
1342 
1343   NavigationControlInterceptorBadPortalActivateResult interceptor_{this};
1344 
1345   friend class RenderFrameHostFactoryForNavigationControlInterceptor;
1346 };
1347 
1348 class RenderFrameHostFactoryForNavigationControlInterceptor
1349     : public TestRenderFrameHostFactory {
1350  protected:
CreateRenderFrameHost(SiteInstance * site_instance,scoped_refptr<RenderViewHostImpl> render_view_host,RenderFrameHostDelegate * delegate,FrameTree * frame_tree,FrameTreeNode * frame_tree_node,int32_t routing_id,bool renderer_initiated_creation)1351   std::unique_ptr<RenderFrameHostImpl> CreateRenderFrameHost(
1352       SiteInstance* site_instance,
1353       scoped_refptr<RenderViewHostImpl> render_view_host,
1354       RenderFrameHostDelegate* delegate,
1355       FrameTree* frame_tree,
1356       FrameTreeNode* frame_tree_node,
1357       int32_t routing_id,
1358       bool renderer_initiated_creation) override {
1359     return base::WrapUnique(
1360         new RenderFrameHostImplForNavigationControlInterceptor(
1361             site_instance, std::move(render_view_host), delegate, frame_tree,
1362             frame_tree_node, routing_id, renderer_initiated_creation));
1363   }
1364 };
1365 
1366 }  // namespace
1367 
1368 // Tests that the browser filters the renderer's replies to the portal
1369 // activation event and terminates misbehaving renderers.
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,MisbehavingRendererActivated)1370 IN_PROC_BROWSER_TEST_F(PortalBrowserTest, MisbehavingRendererActivated) {
1371   EXPECT_TRUE(NavigateToURL(
1372       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
1373   WebContentsImpl* web_contents_impl =
1374       static_cast<WebContentsImpl*>(shell()->web_contents());
1375   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
1376 
1377   // Arrange for a special kind of RenderFrameHost to be created which permits
1378   // its NavigationControl messages to be intercepted.
1379   RenderFrameHostFactoryForNavigationControlInterceptor scoped_rfh_factory;
1380   GURL url = embedded_test_server()->GetURL("a.com", "/title2.html");
1381   Portal* portal = CreatePortalToUrl(web_contents_impl, url);
1382 
1383   // Then activate the portal. Due to the apparent misbehavior from the
1384   // renderer, the caller should be informed that it was aborted due to a bug,
1385   // and the portal's renderer (having been framed for the crime) should be
1386   // killed.
1387   PortalActivatedObserver activated_observer(portal);
1388   RenderProcessHostBadIpcMessageWaiter kill_waiter(
1389       portal->GetPortalContents()->GetMainFrame()->GetProcess());
1390   ExecuteScriptAsync(main_frame,
1391                      "document.querySelector('portal').activate();");
1392   EXPECT_EQ(blink::mojom::PortalActivateResult::kAbortedDueToBug,
1393             activated_observer.WaitForActivateResult());
1394   EXPECT_EQ(bad_message::RPH_MOJO_PROCESS_ERROR, kill_waiter.Wait());
1395 }
1396 
1397 // Test that user facing session history is retained after a portal activation.
1398 // Before activation, we have the host WebContents's navigation entries, which
1399 // is the session history presented to the user, plus the portal WebContents's
1400 // navigation entry, which is not presented as part of that session history.
1401 // Upon activation, the host WebContents's navigation entries are merged into
1402 // the activated portal's WebContents. The resulting session history in the
1403 // activated WebContents is presented to the user.
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,PortalHistoryWithActivation)1404 IN_PROC_BROWSER_TEST_F(PortalBrowserTest, PortalHistoryWithActivation) {
1405   // We have an additional navigation entry in the portal host's contents, so
1406   // that we test that we're retaining more than just the last committed entry
1407   // of the portal host.
1408   GURL previous_url(
1409       embedded_test_server()->GetURL("portal.test", "/title1.html"));
1410   EXPECT_TRUE(NavigateToURL(shell(), previous_url));
1411 
1412   GURL main_url(embedded_test_server()->GetURL("portal.test", "/title2.html"));
1413   EXPECT_TRUE(NavigateToURL(shell(), main_url));
1414 
1415   WebContentsImpl* web_contents_impl =
1416       static_cast<WebContentsImpl*>(shell()->web_contents());
1417   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
1418 
1419   GURL portal_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
1420   Portal* portal = CreatePortalToUrl(web_contents_impl, portal_url);
1421   WebContentsImpl* portal_contents = portal->GetPortalContents();
1422 
1423   NavigationControllerImpl& main_controller =
1424       web_contents_impl->GetController();
1425   NavigationControllerImpl& portal_controller =
1426       portal_contents->GetController();
1427 
1428   EXPECT_EQ(2, main_controller.GetEntryCount());
1429   ASSERT_TRUE(main_controller.GetLastCommittedEntry());
1430   EXPECT_EQ(main_url, main_controller.GetLastCommittedEntry()->GetURL());
1431   EXPECT_EQ(1, portal_controller.GetEntryCount());
1432   ASSERT_TRUE(portal_controller.GetLastCommittedEntry());
1433   EXPECT_EQ(portal_url, portal_controller.GetLastCommittedEntry()->GetURL());
1434 
1435   PortalActivatedObserver activated_observer(portal);
1436   ASSERT_TRUE(
1437       ExecJs(main_frame, "document.querySelector('portal').activate();"));
1438   activated_observer.WaitForActivate();
1439 
1440   NavigationControllerImpl& activated_controller = portal_controller;
1441 
1442   ASSERT_EQ(3, activated_controller.GetEntryCount());
1443   ASSERT_EQ(2, activated_controller.GetLastCommittedEntryIndex());
1444   EXPECT_EQ(previous_url, activated_controller.GetEntryAtIndex(0)->GetURL());
1445   EXPECT_EQ(main_url, activated_controller.GetEntryAtIndex(1)->GetURL());
1446   EXPECT_EQ(portal_url, activated_controller.GetEntryAtIndex(2)->GetURL());
1447 }
1448 
1449 // Test that we may go back/forward across a portal activation as though it
1450 // were a regular navigation.
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,PortalHistoryActivateAndGoBackForward)1451 IN_PROC_BROWSER_TEST_F(PortalBrowserTest,
1452                        PortalHistoryActivateAndGoBackForward) {
1453   GURL main_url(embedded_test_server()->GetURL("portal.test", "/title1.html"));
1454   EXPECT_TRUE(NavigateToURL(shell(), main_url));
1455   WebContentsImpl* web_contents_impl =
1456       static_cast<WebContentsImpl*>(shell()->web_contents());
1457   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
1458 
1459   GURL portal_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
1460   Portal* portal = CreatePortalToUrl(web_contents_impl, portal_url);
1461 
1462   PortalActivatedObserver activated_observer(portal);
1463   ASSERT_TRUE(
1464       ExecJs(main_frame, "document.querySelector('portal').activate();"));
1465   activated_observer.WaitForActivate();
1466 
1467   web_contents_impl = static_cast<WebContentsImpl*>(shell()->web_contents());
1468   NavigationControllerImpl& controller = web_contents_impl->GetController();
1469 
1470   ASSERT_TRUE(controller.CanGoBack());
1471   TestNavigationObserver go_back_observer(web_contents_impl);
1472   controller.GoBack();
1473   go_back_observer.Wait();
1474   // These back/forward navigations do not involve a contents swap, since the
1475   // original contents is gone as it was not adopted.
1476   ASSERT_EQ(web_contents_impl, shell()->web_contents());
1477 
1478   ASSERT_EQ(2, controller.GetEntryCount());
1479   ASSERT_EQ(0, controller.GetLastCommittedEntryIndex());
1480   EXPECT_EQ(main_url, controller.GetEntryAtIndex(0)->GetURL());
1481   EXPECT_EQ(portal_url, controller.GetEntryAtIndex(1)->GetURL());
1482 
1483   ASSERT_TRUE(controller.CanGoForward());
1484   TestNavigationObserver go_forward_observer(web_contents_impl);
1485   controller.GoForward();
1486   go_forward_observer.Wait();
1487   ASSERT_EQ(web_contents_impl, shell()->web_contents());
1488 
1489   ASSERT_EQ(2, controller.GetEntryCount());
1490   ASSERT_EQ(1, controller.GetLastCommittedEntryIndex());
1491   EXPECT_EQ(main_url, controller.GetEntryAtIndex(0)->GetURL());
1492   EXPECT_EQ(portal_url, controller.GetEntryAtIndex(1)->GetURL());
1493 }
1494 
1495 // Activation does not cancel new pending navigations in portals.
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,ActivationWithPortalPendingNavigation)1496 IN_PROC_BROWSER_TEST_F(PortalBrowserTest,
1497                        ActivationWithPortalPendingNavigation) {
1498   GURL main_url(embedded_test_server()->GetURL("portal.test", "/title1.html"));
1499   EXPECT_TRUE(NavigateToURL(shell(), main_url));
1500   WebContentsImpl* web_contents_impl =
1501       static_cast<WebContentsImpl*>(shell()->web_contents());
1502   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
1503 
1504   GURL portal_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
1505   Portal* portal = CreatePortalToUrl(web_contents_impl, portal_url);
1506   WebContentsImpl* portal_contents = portal->GetPortalContents();
1507 
1508   NavigationControllerImpl& portal_controller =
1509       portal_contents->GetController();
1510 
1511   // Have the portal navigate so that we have a pending navigation.
1512   GURL pending_url(embedded_test_server()->GetURL("a.com", "/title2.html"));
1513   TestNavigationManager pending_navigation(portal_contents, pending_url);
1514   EXPECT_TRUE(ExecJs(
1515       main_frame,
1516       JsReplace("document.querySelector('portal').src = $1;", pending_url)));
1517   EXPECT_TRUE(pending_navigation.WaitForRequestStart());
1518 
1519   // Navigating via frame proxy does not create a pending NavigationEntry. We'll
1520   // check for an ongoing NavigationRequest instead.
1521   FrameTreeNode* portal_node =
1522       portal_contents->GetMainFrame()->frame_tree_node();
1523   NavigationRequest* navigation_request = portal_node->navigation_request();
1524   ASSERT_TRUE(navigation_request);
1525 
1526   PortalActivatedObserver activated_observer(portal);
1527   ASSERT_TRUE(
1528       ExecJs(main_frame, "document.querySelector('portal').activate();"));
1529   activated_observer.WaitForActivate();
1530 
1531   NavigationControllerImpl& activated_controller = portal_controller;
1532 
1533   pending_navigation.WaitForNavigationFinished();
1534   ASSERT_EQ(2, activated_controller.GetEntryCount());
1535   ASSERT_EQ(1, activated_controller.GetLastCommittedEntryIndex());
1536   EXPECT_EQ(main_url, activated_controller.GetEntryAtIndex(0)->GetURL());
1537   EXPECT_EQ(pending_url, activated_controller.GetEntryAtIndex(1)->GetURL());
1538 }
1539 
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,DidFocusIPCFromFrameInsidePortal)1540 IN_PROC_BROWSER_TEST_F(PortalBrowserTest, DidFocusIPCFromFrameInsidePortal) {
1541   EXPECT_TRUE(NavigateToURL(
1542       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
1543   WebContentsImpl* web_contents_impl =
1544       static_cast<WebContentsImpl*>(shell()->web_contents());
1545   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
1546 
1547   GURL url = embedded_test_server()->GetURL("a.com", "/title1.html");
1548   Portal* portal = CreatePortalToUrl(web_contents_impl, url);
1549   WebContentsImpl* portal_contents = portal->GetPortalContents();
1550   RenderFrameHostImpl* portal_main_frame = portal_contents->GetMainFrame();
1551 
1552   GURL b_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
1553   TestNavigationObserver iframe_navigation_observer(portal_contents);
1554   EXPECT_TRUE(ExecJs(portal_main_frame,
1555                      JsReplace("var iframe = document.createElement('iframe');"
1556                                "iframe.src = $1;"
1557                                "document.body.appendChild(iframe);",
1558                                b_url)));
1559   iframe_navigation_observer.Wait();
1560   EXPECT_EQ(b_url, iframe_navigation_observer.last_navigation_url());
1561 
1562   web_contents_impl->SetAsFocusedWebContentsIfNecessary();
1563   EXPECT_EQ(web_contents_impl->GetFocusedWebContents(), web_contents_impl);
1564   EXPECT_EQ(web_contents_impl->GetFocusedFrame(), main_frame);
1565 
1566   // Simulate renderer sending LocalFrameHost::DidFocusFrame IPC.
1567   RenderFrameHostImpl* iframe_rfhi =
1568       portal_main_frame->child_at(0)->current_frame_host();
1569   iframe_rfhi->DidFocusFrame();
1570 
1571   // Focus should not have changed.
1572   EXPECT_EQ(web_contents_impl->GetFocusedWebContents(), web_contents_impl);
1573   EXPECT_EQ(web_contents_impl->GetFocusedFrame(), main_frame);
1574 
1575   if (!SiteIsolationPolicy::UseDedicatedProcessesForAllSites())
1576     return;
1577 
1578   // Simulate renderer sending RemoteFrameHost::DidFocusFrame IPC.
1579   RenderFrameProxyHost* iframe_rfph =
1580       portal_main_frame->child_at(0)->render_manager()->GetRenderFrameProxyHost(
1581           portal_main_frame->GetSiteInstance());
1582   iframe_rfph->DidFocusFrame();
1583 
1584   // Focus should not have changed.
1585   EXPECT_EQ(web_contents_impl->GetFocusedWebContents(), web_contents_impl);
1586   EXPECT_EQ(web_contents_impl->GetFocusedFrame(), main_frame);
1587 }
1588 
1589 // Test that a renderer process is killed if it sends an AdvanceFocus IPC to
1590 // advance focus into a portal.
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,AdvanceFocusIntoPortal)1591 IN_PROC_BROWSER_TEST_F(PortalBrowserTest, AdvanceFocusIntoPortal) {
1592   EXPECT_TRUE(NavigateToURL(
1593       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
1594   WebContentsImpl* web_contents_impl =
1595       static_cast<WebContentsImpl*>(shell()->web_contents());
1596   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
1597 
1598   GURL url = embedded_test_server()->GetURL("a.com", "/title1.html");
1599   Portal* portal = CreatePortalToUrl(web_contents_impl, url);
1600   WebContentsImpl* portal_contents = portal->GetPortalContents();
1601   RenderFrameHostImpl* portal_main_frame = portal_contents->GetMainFrame();
1602 
1603   RenderFrameProxyHost* outer_delegate_proxy =
1604       portal_main_frame->frame_tree_node()
1605           ->render_manager()
1606           ->GetProxyToOuterDelegate();
1607   RenderProcessHostBadIpcMessageWaiter rph_kill_waiter(
1608       main_frame->GetProcess());
1609   outer_delegate_proxy->OnMessageReceived(FrameHostMsg_AdvanceFocus(
1610       outer_delegate_proxy->GetRoutingID(), blink::mojom::FocusType::kNone,
1611       main_frame->GetRoutingID()));
1612   base::Optional<bad_message::BadMessageReason> result = rph_kill_waiter.Wait();
1613   EXPECT_TRUE(result.has_value());
1614   EXPECT_EQ(result.value(), bad_message::RFPH_ADVANCE_FOCUS_INTO_PORTAL);
1615 }
1616 
1617 namespace {
WaitForAccessibilityTree(WebContents * web_contents)1618 void WaitForAccessibilityTree(WebContents* web_contents) {
1619   AccessibilityNotificationWaiter waiter(web_contents, ui::kAXModeComplete,
1620                                          ax::mojom::Event::kNone);
1621   waiter.WaitForNotification();
1622 }
1623 }  // namespace
1624 
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,RootAccessibilityManagerShouldUpdateAfterActivation)1625 IN_PROC_BROWSER_TEST_F(PortalBrowserTest,
1626                        RootAccessibilityManagerShouldUpdateAfterActivation) {
1627   EXPECT_TRUE(NavigateToURL(
1628       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
1629   WebContentsImpl* web_contents_impl =
1630       static_cast<WebContentsImpl*>(shell()->web_contents());
1631   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
1632   WaitForAccessibilityTree(web_contents_impl);
1633 
1634   // Create portal.
1635   GURL url = embedded_test_server()->GetURL("a.com", "/title1.html");
1636   Portal* portal = CreatePortalToUrl(web_contents_impl, url);
1637   WebContentsImpl* portal_contents = portal->GetPortalContents();
1638   RenderFrameHostImpl* portal_frame = portal_contents->GetMainFrame();
1639   WaitForAccessibilityTree(portal_contents);
1640 
1641   EXPECT_NE(nullptr, portal_frame->browser_accessibility_manager());
1642   EXPECT_EQ(main_frame->browser_accessibility_manager(),
1643             portal_frame->browser_accessibility_manager()->GetRootManager());
1644 
1645   // Activate portal and adopt predecessor.
1646   EXPECT_TRUE(ExecJs(portal_frame,
1647                      "window.addEventListener('portalactivate', e => { "
1648                      "  var portal = e.adoptPredecessor(); "
1649                      "  document.body.appendChild(portal); "
1650                      "});"));
1651   {
1652     PortalActivatedObserver activated_observer(portal);
1653     PortalCreatedObserver adoption_observer(portal_frame);
1654     EXPECT_TRUE(ExecJs(main_frame,
1655                        "let portal = document.querySelector('portal');"
1656                        "portal.activate().then(() => { "
1657                        "  document.body.removeChild(portal); "
1658                        "});"));
1659     EXPECT_EQ(blink::mojom::PortalActivateResult::kPredecessorWasAdopted,
1660               activated_observer.WaitForActivateResult());
1661     adoption_observer.WaitUntilPortalCreated();
1662   }
1663 
1664   EXPECT_EQ(portal_frame->browser_accessibility_manager()->GetRootManager(),
1665             portal_frame->browser_accessibility_manager());
1666 }
1667 
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,CrossSitePortalNavCommitsAfterActivation)1668 IN_PROC_BROWSER_TEST_F(PortalBrowserTest,
1669                        CrossSitePortalNavCommitsAfterActivation) {
1670   ASSERT_TRUE(NavigateToURL(
1671       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
1672   WebContentsImpl* web_contents_impl =
1673       static_cast<WebContentsImpl*>(shell()->web_contents());
1674   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
1675 
1676   GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
1677   Portal* portal = CreatePortalToUrl(web_contents_impl, a_url);
1678 
1679   TestNavigationObserver nav_observer(portal->GetPortalContents());
1680   GURL b_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
1681   // Start a cross site navigation in the portal contents and immediately
1682   // activate the portal. The navigation starts in a child frame but commits
1683   // after the frame becomes the root frame.
1684   ExecuteScriptAsync(main_frame,
1685                      JsReplace("let portal = document.querySelector('portal');"
1686                                "portal.src = $1;"
1687                                "portal.activate();",
1688                                b_url));
1689   nav_observer.Wait();
1690 }
1691 
1692 // This is similar to CrossSitePortalNavCommitsAfterActivation, but we navigate
1693 // an adopted predecessor that hasn't been attached and the navigation commits
1694 // after the predecessor is reactivated.
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,CrossSitePortalNavInUnattachedPredecessorCommitsAfterActivation)1695 IN_PROC_BROWSER_TEST_F(
1696     PortalBrowserTest,
1697     CrossSitePortalNavInUnattachedPredecessorCommitsAfterActivation) {
1698   ASSERT_TRUE(NavigateToURL(
1699       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
1700   WebContentsImpl* web_contents_impl =
1701       static_cast<WebContentsImpl*>(shell()->web_contents());
1702   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
1703 
1704   GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
1705   Portal* portal = CreatePortalToUrl(web_contents_impl, a_url);
1706   WebContentsImpl* portal_contents = portal->GetPortalContents();
1707   RenderFrameHostImpl* portal_frame = portal_contents->GetMainFrame();
1708 
1709   GURL b_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
1710   EXPECT_TRUE(
1711       ExecJs(portal_frame,
1712              JsReplace("window.addEventListener('portalactivate', (e) => {"
1713                        "  let predecessor = e.adoptPredecessor();"
1714                        "  predecessor.src = $1;"
1715                        "  predecessor.activate();"
1716                        "});",
1717                        b_url)));
1718 
1719   TestNavigationObserver nav_observer(web_contents_impl);
1720   ExecuteScriptAsync(main_frame,
1721                      "document.querySelector('portal').activate();");
1722   nav_observer.Wait();
1723 }
1724 
1725 // Ensure portal activations respect navigation precedence. If there is an
1726 // ongoing browser initiated navigation, then a portal activation without user
1727 // activation cannot proceed.
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,NavigationPrecedence)1728 IN_PROC_BROWSER_TEST_F(PortalBrowserTest, NavigationPrecedence) {
1729   GURL main_url1(embedded_test_server()->GetURL("portal.test", "/title1.html"));
1730   GURL main_url2(embedded_test_server()->GetURL("portal.test", "/title2.html"));
1731   ASSERT_TRUE(NavigateToURL(shell(), main_url1));
1732   ASSERT_TRUE(NavigateToURL(shell(), main_url2));
1733   WebContentsImpl* web_contents_impl =
1734       static_cast<WebContentsImpl*>(shell()->web_contents());
1735   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
1736 
1737   GURL portal_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
1738   CreatePortalToUrl(web_contents_impl, portal_url);
1739 
1740   TestNavigationManager pending_back_navigation(web_contents_impl, main_url1);
1741   ASSERT_TRUE(web_contents_impl->GetController().CanGoBack());
1742   web_contents_impl->GetController().GoBack();
1743   EXPECT_TRUE(pending_back_navigation.WaitForRequestStart());
1744 
1745   EXPECT_EQ("reject", EvalJs(main_frame,
1746                              "document.querySelector('portal').activate().then("
1747                              "    () => 'resolve', () => 'reject');",
1748                              EXECUTE_SCRIPT_NO_USER_GESTURE));
1749 
1750   pending_back_navigation.WaitForNavigationFinished();
1751   EXPECT_TRUE(pending_back_navigation.was_successful());
1752 }
1753 
IN_PROC_BROWSER_TEST_F(PortalBrowserTest,CallCreateProxyAndAttachPortalTwice)1754 IN_PROC_BROWSER_TEST_F(PortalBrowserTest, CallCreateProxyAndAttachPortalTwice) {
1755   EXPECT_TRUE(NavigateToURL(
1756       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
1757   WebContentsImpl* web_contents_impl =
1758       static_cast<WebContentsImpl*>(shell()->web_contents());
1759   RenderFrameHostImpl* main_frame = web_contents_impl->GetMainFrame();
1760 
1761   GURL url = embedded_test_server()->GetURL("a.com", "/title1.html");
1762   Portal* portal = CreatePortalToUrl(web_contents_impl, url);
1763   PortalInterceptorForTesting* portal_interceptor =
1764       PortalInterceptorForTesting::From(portal);
1765   // Hijacks navigate request and calls CreateProxyAndAttachPortal instead.
1766   portal_interceptor->SetNavigateCallback(base::BindRepeating(
1767       [](Portal* portal, const GURL& url, blink::mojom::ReferrerPtr referrer,
1768          blink::mojom::Portal::NavigateCallback callback) {
1769         portal->CreateProxyAndAttachPortal();
1770         std::move(callback).Run();
1771       },
1772       portal));
1773 
1774   RenderProcessHostBadIpcMessageWaiter rph_kill_waiter(
1775       main_frame->GetProcess());
1776   GURL dummy_url = embedded_test_server()->GetURL("b.com", "/title1.html");
1777   ExecuteScriptAsync(
1778       main_frame,
1779       JsReplace("document.querySelector('portal').src = $1", dummy_url));
1780   EXPECT_EQ(bad_message::RPH_MOJO_PROCESS_ERROR, rph_kill_waiter.Wait());
1781 }
1782 
1783 class PortalOOPIFBrowserTest : public PortalBrowserTest {
1784  protected:
PortalOOPIFBrowserTest()1785   PortalOOPIFBrowserTest() {}
1786 
SetUpCommandLine(base::CommandLine * command_line)1787   void SetUpCommandLine(base::CommandLine* command_line) override {
1788     IsolateAllSitesForTesting(command_line);
1789   }
1790 };
1791 
1792 // Tests that creating and destroying OOPIFs inside the portal works as
1793 // intended.
IN_PROC_BROWSER_TEST_F(PortalOOPIFBrowserTest,OOPIFInsidePortal)1794 IN_PROC_BROWSER_TEST_F(PortalOOPIFBrowserTest, OOPIFInsidePortal) {
1795   EXPECT_TRUE(NavigateToURL(
1796       shell(), embedded_test_server()->GetURL("portal.test", "/title1.html")));
1797   WebContentsImpl* web_contents_impl =
1798       static_cast<WebContentsImpl*>(shell()->web_contents());
1799 
1800   GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
1801   Portal* portal = CreatePortalToUrl(web_contents_impl, a_url);
1802   WebContentsImpl* portal_contents = portal->GetPortalContents();
1803   RenderFrameHostImpl* portal_main_frame = portal_contents->GetMainFrame();
1804 
1805   // Add an out-of-process iframe to the portal.
1806   GURL b_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
1807   TestNavigationObserver iframe_navigation_observer(portal_contents);
1808   EXPECT_TRUE(ExecJs(portal_main_frame,
1809                      JsReplace("var iframe = document.createElement('iframe');"
1810                                "iframe.src = $1;"
1811                                "document.body.appendChild(iframe);",
1812                                b_url)));
1813   iframe_navigation_observer.Wait();
1814   EXPECT_EQ(b_url, iframe_navigation_observer.last_navigation_url());
1815   RenderFrameHostImpl* portal_iframe =
1816       portal_main_frame->child_at(0)->current_frame_host();
1817   EXPECT_NE(portal_main_frame->GetSiteInstance(),
1818             portal_iframe->GetSiteInstance());
1819 
1820   // Remove the OOPIF from the portal.
1821   RenderFrameDeletedObserver deleted_observer(portal_iframe);
1822   EXPECT_TRUE(
1823       ExecJs(portal_main_frame, "document.querySelector('iframe').remove();"));
1824   deleted_observer.WaitUntilDeleted();
1825 }
1826 
1827 }  // namespace content
1828