1 // Copyright 2015 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 "content/browser/frame_host/render_frame_host_impl.h"
6 
7 #include <string>
8 #include <utility>
9 
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/files/file_path.h"
13 #include "base/memory/ptr_util.h"
14 #include "base/path_service.h"
15 #include "base/run_loop.h"
16 #include "base/strings/strcat.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/test/bind_test_util.h"
20 #include "base/test/metrics/histogram_tester.h"
21 #include "base/test/mock_callback.h"
22 #include "base/test/scoped_feature_list.h"
23 #include "base/test/test_timeouts.h"
24 #include "build/build_config.h"
25 #include "content/browser/frame_host/navigation_request.h"
26 #include "content/browser/interface_provider_filtering.h"
27 #include "content/browser/renderer_host/input/timeout_monitor.h"
28 #include "content/browser/renderer_host/render_process_host_impl.h"
29 #include "content/browser/web_contents/web_contents_impl.h"
30 #include "content/common/frame_messages.h"
31 #include "content/public/browser/javascript_dialog_manager.h"
32 #include "content/public/browser/render_frame_host.h"
33 #include "content/public/browser/web_contents.h"
34 #include "content/public/browser/web_contents_observer.h"
35 #include "content/public/common/content_client.h"
36 #include "content/public/common/content_features.h"
37 #include "content/public/common/page_visibility_state.h"
38 #include "content/public/test/browser_test_utils.h"
39 #include "content/public/test/content_browser_test.h"
40 #include "content/public/test/content_browser_test_utils.h"
41 #include "content/public/test/navigation_handle_observer.h"
42 #include "content/public/test/simple_url_loader_test_helper.h"
43 #include "content/public/test/test_frame_navigation_observer.h"
44 #include "content/public/test/test_navigation_observer.h"
45 #include "content/public/test/test_utils.h"
46 #include "content/public/test/url_loader_interceptor.h"
47 #include "content/shell/browser/shell.h"
48 #include "content/test/content_browser_test_utils_internal.h"
49 #include "content/test/data/mojo_web_test_helper_test.mojom.h"
50 #include "content/test/did_commit_navigation_interceptor.h"
51 #include "content/test/frame_host_test_interface.mojom.h"
52 #include "content/test/test_content_browser_client.h"
53 #include "content/test/test_render_frame_host_factory.h"
54 #include "net/base/features.h"
55 #include "net/base/net_errors.h"
56 #include "net/cookies/cookie_constants.h"
57 #include "net/dns/mock_host_resolver.h"
58 #include "net/test/embedded_test_server/controllable_http_response.h"
59 #include "net/test/embedded_test_server/embedded_test_server.h"
60 #include "net/test/embedded_test_server/http_request.h"
61 #include "net/test/embedded_test_server/request_handler_util.h"
62 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
63 #include "services/network/public/cpp/features.h"
64 #include "services/network/public/cpp/resource_request.h"
65 #include "services/network/public/cpp/simple_url_loader.h"
66 #include "services/network/public/mojom/url_loader_factory.mojom.h"
67 #include "services/network/test/test_url_loader_factory.h"
68 #include "services/service_manager/public/cpp/interface_provider.h"
69 #include "testing/gmock/include/gmock/gmock.h"
70 #include "third_party/blink/public/mojom/browser_interface_broker.mojom-test-utils.h"
71 #include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h"
72 #include "third_party/blink/public/mojom/frame/frame.mojom-test-utils.h"
73 #include "url/gurl.h"
74 #include "url/origin.h"
75 
76 #if defined(OS_ANDROID)
77 #include "base/android/build_info.h"
78 #include "third_party/blink/public/mojom/remote_objects/remote_objects.mojom.h"
79 #endif  // defined(OS_ANDROID)
80 
81 namespace content {
82 
83 namespace {
84 
85 // Implementation of ContentBrowserClient that overrides
86 // OverridePageVisibilityState() and allows consumers to set a value.
87 class PrerenderTestContentBrowserClient : public TestContentBrowserClient {
88  public:
PrerenderTestContentBrowserClient()89   PrerenderTestContentBrowserClient()
90       : override_enabled_(false),
91         visibility_override_(PageVisibilityState::kVisible) {}
~PrerenderTestContentBrowserClient()92   ~PrerenderTestContentBrowserClient() override {}
93 
EnableVisibilityOverride(PageVisibilityState visibility_override)94   void EnableVisibilityOverride(PageVisibilityState visibility_override) {
95     override_enabled_ = true;
96     visibility_override_ = visibility_override;
97   }
98 
OverridePageVisibilityState(RenderFrameHost * render_frame_host,PageVisibilityState * visibility_state)99   void OverridePageVisibilityState(
100       RenderFrameHost* render_frame_host,
101       PageVisibilityState* visibility_state) override {
102     if (override_enabled_)
103       *visibility_state = visibility_override_;
104   }
105 
106  private:
107   bool override_enabled_;
108   PageVisibilityState visibility_override_;
109 
110   DISALLOW_COPY_AND_ASSIGN(PrerenderTestContentBrowserClient);
111 };
112 
113 const char kTrustMeUrl[] = "trustme://host/path/";
114 const char kTrustMeIfEmbeddingSecureUrl[] =
115     "trustmeifembeddingsecure://host/path/";
116 
117 // Configure trustme: as a scheme that should cause cookies to be treated as
118 // first-party when top-level, and also installs a URLLoaderFactory that
119 // makes all requests to it via kTrustMeUrl return a particular iframe.
120 // Same for trustmeifembeddingsecure, which does the same if the embedded origin
121 // is secure.
122 class FirstPartySchemeContentBrowserClient : public TestContentBrowserClient {
123  public:
FirstPartySchemeContentBrowserClient(const GURL & iframe_url)124   explicit FirstPartySchemeContentBrowserClient(const GURL& iframe_url)
125       : iframe_url_(iframe_url) {}
126 
127   ~FirstPartySchemeContentBrowserClient() override = default;
128 
ShouldTreatURLSchemeAsFirstPartyWhenTopLevel(base::StringPiece scheme,bool is_embedded_origin_secure)129   bool ShouldTreatURLSchemeAsFirstPartyWhenTopLevel(
130       base::StringPiece scheme,
131       bool is_embedded_origin_secure) override {
132     if (is_embedded_origin_secure && scheme == "trustmeifembeddingsecure")
133       return true;
134     return scheme == "trustme";
135   }
136 
RegisterNonNetworkNavigationURLLoaderFactories(int frame_tree_node_id,NonNetworkURLLoaderFactoryMap * factories)137   void RegisterNonNetworkNavigationURLLoaderFactories(
138       int frame_tree_node_id,
139       NonNetworkURLLoaderFactoryMap* factories) override {
140     auto trustme_factory = std::make_unique<network::TestURLLoaderFactory>();
141     auto trustmeifembeddingsecure_factory =
142         std::make_unique<network::TestURLLoaderFactory>();
143     std::string response_body =
144         base::StrCat({"<iframe src=\"", iframe_url_.spec(), "\"></iframe>"});
145     trustme_factory->AddResponse(kTrustMeUrl, response_body);
146     trustmeifembeddingsecure_factory->AddResponse(kTrustMeIfEmbeddingSecureUrl,
147                                                   response_body);
148     factories->emplace("trustme", std::move(trustme_factory));
149     factories->emplace("trustmeifembeddingsecure",
150                        std::move(trustmeifembeddingsecure_factory));
151   }
152 
153  private:
154   GURL iframe_url_;
155 
156   DISALLOW_COPY_AND_ASSIGN(FirstPartySchemeContentBrowserClient);
157 };
158 
159 }  // anonymous namespace
160 
161 // TODO(mlamouri): part of these tests were removed because they were dependent
162 // on an environment were focus is guaranteed. This is only for
163 // interactive_ui_tests so these bits need to move there.
164 // See https://crbug.com/491535
165 class RenderFrameHostImplBrowserTest : public ContentBrowserTest {
166  public:
RenderFrameHostImplBrowserTest()167   RenderFrameHostImplBrowserTest()
168       : https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
169     // This makes the tests that check NetworkIsolationKeys make sure both the
170     // frame and top frame origins are correct, without significantly affecting
171     // other tests.
172     feature_list_.InitAndEnableFeature(
173         net::features::kAppendFrameOriginToNetworkIsolationKey);
174   }
175   ~RenderFrameHostImplBrowserTest() override = default;
176 
177   // Return an URL for loading a local test file.
GetFileURL(const base::FilePath::CharType * file_path)178   GURL GetFileURL(const base::FilePath::CharType* file_path) {
179     base::FilePath path;
180     CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &path));
181     path = path.Append(GetTestDataFilePath());
182     path = path.Append(file_path);
183     return GURL(FILE_PATH_LITERAL("file:") + path.value());
184   }
185 
186  protected:
SetUpOnMainThread()187   void SetUpOnMainThread() override {
188     host_resolver()->AddRule("*", "127.0.0.1");
189     SetupCrossSiteRedirector(embedded_test_server());
190     ASSERT_TRUE(embedded_test_server()->Start());
191   }
https_server()192   net::EmbeddedTestServer* https_server() { return &https_server_; }
193 
web_contents() const194   WebContentsImpl* web_contents() const {
195     return static_cast<WebContentsImpl*>(shell()->web_contents());
196   }
197 
198  private:
199   base::test::ScopedFeatureList feature_list_;
200   net::EmbeddedTestServer https_server_;
201 };
202 
203 // Test that when creating a new window, the main frame is correctly focused.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,IsFocused_AtLoad)204 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest, IsFocused_AtLoad) {
205   EXPECT_TRUE(
206       NavigateToURL(shell(), GetTestUrl("render_frame_host", "focus.html")));
207 
208   // The main frame should be focused.
209   WebContents* web_contents = shell()->web_contents();
210   EXPECT_EQ(web_contents->GetMainFrame(), web_contents->GetFocusedFrame());
211 }
212 
213 // Test that if the content changes the focused frame, it is correctly exposed.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,IsFocused_Change)214 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest, IsFocused_Change) {
215   EXPECT_TRUE(
216       NavigateToURL(shell(), GetTestUrl("render_frame_host", "focus.html")));
217 
218   WebContents* web_contents = shell()->web_contents();
219 
220   std::string frames[2] = {"frame1", "frame2"};
221   for (const std::string& frame : frames) {
222     ExecuteScriptAndGetValue(web_contents->GetMainFrame(),
223                              "focus" + frame + "()");
224 
225     // The main frame is not the focused frame in the frame tree but the main
226     // frame is focused per RFHI rules because one of its descendant is focused.
227     // TODO(mlamouri): we should check the frame focus state per RFHI, see the
228     // general comment at the beginning of this test file.
229     EXPECT_NE(web_contents->GetMainFrame(), web_contents->GetFocusedFrame());
230     EXPECT_EQ(frame, web_contents->GetFocusedFrame()->GetFrameName());
231   }
232 }
233 
234 // Tests focus behavior when the focused frame is removed from the frame tree.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,RemoveFocusedFrame)235 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest, RemoveFocusedFrame) {
236   EXPECT_TRUE(
237       NavigateToURL(shell(), GetTestUrl("render_frame_host", "focus.html")));
238 
239   WebContentsImpl* web_contents =
240       static_cast<WebContentsImpl*>(shell()->web_contents());
241 
242   ExecuteScriptAndGetValue(web_contents->GetMainFrame(), "focusframe4()");
243 
244   EXPECT_NE(web_contents->GetMainFrame(), web_contents->GetFocusedFrame());
245   EXPECT_EQ("frame4", web_contents->GetFocusedFrame()->GetFrameName());
246   EXPECT_EQ("frame3",
247             web_contents->GetFocusedFrame()->GetParent()->GetFrameName());
248   EXPECT_NE(-1, web_contents->GetFrameTree()->focused_frame_tree_node_id_);
249 
250   ExecuteScriptAndGetValue(web_contents->GetMainFrame(), "detachframe(3)");
251   EXPECT_EQ(nullptr, web_contents->GetFocusedFrame());
252   EXPECT_EQ(-1, web_contents->GetFrameTree()->focused_frame_tree_node_id_);
253 
254   ExecuteScriptAndGetValue(web_contents->GetMainFrame(), "focusframe2()");
255   EXPECT_NE(nullptr, web_contents->GetFocusedFrame());
256   EXPECT_NE(web_contents->GetMainFrame(), web_contents->GetFocusedFrame());
257   EXPECT_NE(-1, web_contents->GetFrameTree()->focused_frame_tree_node_id_);
258 
259   ExecuteScriptAndGetValue(web_contents->GetMainFrame(), "detachframe(2)");
260   EXPECT_EQ(nullptr, web_contents->GetFocusedFrame());
261   EXPECT_EQ(-1, web_contents->GetFrameTree()->focused_frame_tree_node_id_);
262 }
263 
264 // Test that a frame is visible/hidden depending on its WebContents visibility
265 // state.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,GetVisibilityState_Basic)266 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
267                        GetVisibilityState_Basic) {
268   EXPECT_TRUE(NavigateToURL(shell(), GURL("data:text/html,foo")));
269   WebContents* web_contents = shell()->web_contents();
270 
271   web_contents->WasShown();
272   EXPECT_EQ(PageVisibilityState::kVisible,
273             web_contents->GetMainFrame()->GetVisibilityState());
274 
275   web_contents->WasHidden();
276   EXPECT_EQ(PageVisibilityState::kHidden,
277             web_contents->GetMainFrame()->GetVisibilityState());
278 }
279 
280 // Test that a frame visibility can be overridden by the ContentBrowserClient.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,GetVisibilityState_Override)281 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
282                        GetVisibilityState_Override) {
283   EXPECT_TRUE(NavigateToURL(shell(), GURL("data:text/html,foo")));
284   WebContents* web_contents = shell()->web_contents();
285 
286   PrerenderTestContentBrowserClient new_client;
287   ContentBrowserClient* old_client = SetBrowserClientForTesting(&new_client);
288 
289   web_contents->WasShown();
290   EXPECT_EQ(PageVisibilityState::kVisible,
291             web_contents->GetMainFrame()->GetVisibilityState());
292 
293   new_client.EnableVisibilityOverride(PageVisibilityState::kHiddenButPainting);
294   EXPECT_EQ(PageVisibilityState::kHiddenButPainting,
295             web_contents->GetMainFrame()->GetVisibilityState());
296 
297   SetBrowserClientForTesting(old_client);
298 }
299 
300 // Check that the URLLoaderFactories created by RenderFrameHosts for renderers
301 // are not trusted.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,URLLoaderFactoryNotTrusted)302 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
303                        URLLoaderFactoryNotTrusted) {
304   EXPECT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL("/echo")));
305   mojo::Remote<network::mojom::URLLoaderFactory> url_loader_factory;
306   shell()->web_contents()->GetMainFrame()->CreateNetworkServiceDefaultFactory(
307       url_loader_factory.BindNewPipeAndPassReceiver());
308 
309   std::unique_ptr<network::ResourceRequest> request =
310       std::make_unique<network::ResourceRequest>();
311   request->url = embedded_test_server()->GetURL("/echo");
312   request->request_initiator =
313       url::Origin::Create(embedded_test_server()->base_url());
314   request->trusted_params = network::ResourceRequest::TrustedParams();
315 
316   content::SimpleURLLoaderTestHelper simple_loader_helper;
317   std::unique_ptr<network::SimpleURLLoader> simple_loader =
318       network::SimpleURLLoader::Create(std::move(request),
319                                        TRAFFIC_ANNOTATION_FOR_TESTS);
320   simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
321       url_loader_factory.get(), simple_loader_helper.GetCallback());
322   simple_loader_helper.WaitForCallback();
323   EXPECT_FALSE(simple_loader_helper.response_body());
324   EXPECT_EQ(net::ERR_INVALID_ARGUMENT, simple_loader->NetError());
325 }
326 
327 namespace {
328 
329 class TestJavaScriptDialogManager : public JavaScriptDialogManager,
330                                     public WebContentsDelegate {
331  public:
TestJavaScriptDialogManager()332   TestJavaScriptDialogManager()
333       : message_loop_runner_(new MessageLoopRunner), url_invalidate_count_(0) {}
~TestJavaScriptDialogManager()334   ~TestJavaScriptDialogManager() override {}
335 
336   // This waits until either WCD::BeforeUnloadFired is called (the unload has
337   // been handled) or JSDM::RunJavaScriptDialog/RunBeforeUnloadDialog is called
338   // (a request to display a dialog has been received).
Wait()339   void Wait() {
340     message_loop_runner_->Run();
341     message_loop_runner_ = new MessageLoopRunner;
342   }
343 
344   // Runs the dialog callback.
Run(bool success,const base::string16 & user_input)345   void Run(bool success, const base::string16& user_input) {
346     std::move(callback_).Run(success, user_input);
347   }
348 
num_beforeunload_dialogs_seen()349   int num_beforeunload_dialogs_seen() { return num_beforeunload_dialogs_seen_; }
num_beforeunload_fired_seen()350   int num_beforeunload_fired_seen() { return num_beforeunload_fired_seen_; }
proceed()351   bool proceed() { return proceed_; }
352 
353   // WebContentsDelegate
354 
GetJavaScriptDialogManager(WebContents * source)355   JavaScriptDialogManager* GetJavaScriptDialogManager(
356       WebContents* source) override {
357     return this;
358   }
359 
BeforeUnloadFired(WebContents * tab,bool proceed,bool * proceed_to_fire_unload)360   void BeforeUnloadFired(WebContents* tab,
361                          bool proceed,
362                          bool* proceed_to_fire_unload) override {
363     ++num_beforeunload_fired_seen_;
364     proceed_ = proceed;
365     message_loop_runner_->Quit();
366   }
367 
368   // JavaScriptDialogManager
369 
RunJavaScriptDialog(WebContents * web_contents,RenderFrameHost * render_frame_host,JavaScriptDialogType dialog_type,const base::string16 & message_text,const base::string16 & default_prompt_text,DialogClosedCallback callback,bool * did_suppress_message)370   void RunJavaScriptDialog(WebContents* web_contents,
371                            RenderFrameHost* render_frame_host,
372                            JavaScriptDialogType dialog_type,
373                            const base::string16& message_text,
374                            const base::string16& default_prompt_text,
375                            DialogClosedCallback callback,
376                            bool* did_suppress_message) override {
377     callback_ = std::move(callback);
378     message_loop_runner_->Quit();
379   }
380 
RunBeforeUnloadDialog(WebContents * web_contents,RenderFrameHost * render_frame_host,bool is_reload,DialogClosedCallback callback)381   void RunBeforeUnloadDialog(WebContents* web_contents,
382                              RenderFrameHost* render_frame_host,
383                              bool is_reload,
384                              DialogClosedCallback callback) override {
385     ++num_beforeunload_dialogs_seen_;
386     callback_ = std::move(callback);
387     message_loop_runner_->Quit();
388   }
389 
HandleJavaScriptDialog(WebContents * web_contents,bool accept,const base::string16 * prompt_override)390   bool HandleJavaScriptDialog(WebContents* web_contents,
391                               bool accept,
392                               const base::string16* prompt_override) override {
393     return true;
394   }
395 
CancelDialogs(WebContents * web_contents,bool reset_state)396   void CancelDialogs(WebContents* web_contents, bool reset_state) override {}
397 
398   // Keep track of whether the tab has notified us of a navigation state change
399   // which invalidates the displayed URL.
NavigationStateChanged(WebContents * source,InvalidateTypes changed_flags)400   void NavigationStateChanged(WebContents* source,
401                               InvalidateTypes changed_flags) override {
402     if (changed_flags & INVALIDATE_TYPE_URL)
403       url_invalidate_count_++;
404   }
405 
url_invalidate_count()406   int url_invalidate_count() { return url_invalidate_count_; }
reset_url_invalidate_count()407   void reset_url_invalidate_count() { url_invalidate_count_ = 0; }
408 
409  private:
410   DialogClosedCallback callback_;
411 
412   // The MessageLoopRunner used to spin the message loop.
413   scoped_refptr<MessageLoopRunner> message_loop_runner_;
414 
415   // The number of times NavigationStateChanged has been called.
416   int url_invalidate_count_;
417 
418   // The total number of beforeunload dialogs seen by this dialog manager.
419   int num_beforeunload_dialogs_seen_ = 0;
420 
421   // The total number of BeforeUnloadFired events witnessed by the
422   // WebContentsDelegate.
423   int num_beforeunload_fired_seen_ = 0;
424 
425   // The |proceed| value returned by the last unload event.
426   bool proceed_ = false;
427 
428   DISALLOW_COPY_AND_ASSIGN(TestJavaScriptDialogManager);
429 };
430 
431 // A RenderFrameHostImpl that discards callback for BeforeUnload.
432 class RenderFrameHostImplForBeforeUnloadInterceptor
433     : public RenderFrameHostImpl {
434  public:
435   using RenderFrameHostImpl::RenderFrameHostImpl;
436 
SendBeforeUnload(bool is_reload,base::WeakPtr<RenderFrameHostImpl> rfh)437   void SendBeforeUnload(bool is_reload,
438                         base::WeakPtr<RenderFrameHostImpl> rfh) override {
439     rfh->GetAssociatedLocalFrame()->BeforeUnload(is_reload, base::DoNothing());
440   }
441 
442  private:
443   friend class RenderFrameHostFactoryForBeforeUnloadInterceptor;
444 };
445 
446 class RenderFrameHostFactoryForBeforeUnloadInterceptor
447     : public TestRenderFrameHostFactory {
448  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)449   std::unique_ptr<RenderFrameHostImpl> CreateRenderFrameHost(
450       SiteInstance* site_instance,
451       scoped_refptr<RenderViewHostImpl> render_view_host,
452       RenderFrameHostDelegate* delegate,
453       FrameTree* frame_tree,
454       FrameTreeNode* frame_tree_node,
455       int32_t routing_id,
456       bool renderer_initiated_creation) override {
457     return base::WrapUnique(new RenderFrameHostImplForBeforeUnloadInterceptor(
458         site_instance, std::move(render_view_host), delegate, frame_tree,
459         frame_tree_node, routing_id, renderer_initiated_creation));
460   }
461 };
462 
CreateDisconnectedMessagePipeHandle()463 mojo::ScopedMessagePipeHandle CreateDisconnectedMessagePipeHandle() {
464   mojo::MessagePipe pipe;
465   return std::move(pipe.handle0);
466 }
467 
468 }  // namespace
469 
470 // Tests that a beforeunload dialog in an iframe doesn't stop the beforeunload
471 // timer of a parent frame.
472 // TODO(avi): flaky on Linux TSAN: http://crbug.com/795326
473 #if defined(OS_LINUX) && defined(THREAD_SANITIZER)
474 #define MAYBE_IframeBeforeUnloadParentHang DISABLED_IframeBeforeUnloadParentHang
475 #else
476 #define MAYBE_IframeBeforeUnloadParentHang IframeBeforeUnloadParentHang
477 #endif
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,MAYBE_IframeBeforeUnloadParentHang)478 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
479                        MAYBE_IframeBeforeUnloadParentHang) {
480   RenderFrameHostFactoryForBeforeUnloadInterceptor interceptor;
481 
482   WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
483   TestJavaScriptDialogManager dialog_manager;
484   wc->SetDelegate(&dialog_manager);
485 
486   EXPECT_TRUE(NavigateToURL(shell(), GURL("about:blank")));
487   // Make an iframe with a beforeunload handler.
488   std::string script =
489       "var iframe = document.createElement('iframe');"
490       "document.body.appendChild(iframe);"
491       "iframe.contentWindow.onbeforeunload=function(e){return 'x'};";
492   EXPECT_TRUE(content::ExecuteScript(wc, script));
493   EXPECT_TRUE(WaitForLoadStop(wc));
494   // JavaScript onbeforeunload dialogs require a user gesture.
495   for (auto* frame : wc->GetAllFrames())
496     frame->ExecuteJavaScriptWithUserGestureForTests(base::string16());
497 
498   // Force a process switch by going to a privileged page. The beforeunload
499   // timer will be started on the top-level frame but will be paused while the
500   // beforeunload dialog is shown by the subframe.
501   GURL web_ui_page(std::string(kChromeUIScheme) + "://" +
502                    std::string(kChromeUIGpuHost));
503   shell()->LoadURL(web_ui_page);
504   dialog_manager.Wait();
505 
506   RenderFrameHostImpl* main_frame =
507       static_cast<RenderFrameHostImpl*>(wc->GetMainFrame());
508   EXPECT_TRUE(main_frame->is_waiting_for_beforeunload_completion());
509 
510   // Answer the dialog.
511   dialog_manager.Run(true, base::string16());
512 
513   // There will be no beforeunload completion callback invocation, so if the
514   // beforeunload completion callback timer isn't functioning then the
515   // navigation will hang forever and this test will time out. If this waiting
516   // for the load stop works, this test won't time out.
517   EXPECT_TRUE(WaitForLoadStop(wc));
518   EXPECT_EQ(web_ui_page, wc->GetLastCommittedURL());
519 
520   wc->SetDelegate(nullptr);
521   wc->SetJavaScriptDialogManagerForTesting(nullptr);
522 }
523 
524 // Tests that a gesture is required in a frame before it can request a
525 // beforeunload dialog.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,BeforeUnloadDialogRequiresGesture)526 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
527                        BeforeUnloadDialogRequiresGesture) {
528   WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
529   TestJavaScriptDialogManager dialog_manager;
530   wc->SetDelegate(&dialog_manager);
531 
532   EXPECT_TRUE(NavigateToURL(
533       shell(), GetTestUrl("render_frame_host", "beforeunload.html")));
534   // Disable the hang monitor, otherwise there will be a race between the
535   // beforeunload dialog and the beforeunload hang timer.
536   wc->GetMainFrame()->DisableBeforeUnloadHangMonitorForTesting();
537 
538   // Reload. There should be no beforeunload dialog because there was no gesture
539   // on the page. If there was, this WaitForLoadStop call will hang.
540   wc->GetController().Reload(ReloadType::NORMAL, false);
541   EXPECT_TRUE(WaitForLoadStop(wc));
542 
543   // Give the page a user gesture and try reloading again. This time there
544   // should be a dialog. If there is no dialog, the call to Wait will hang.
545   wc->GetMainFrame()->ExecuteJavaScriptWithUserGestureForTests(
546       base::string16());
547   wc->GetController().Reload(ReloadType::NORMAL, false);
548   dialog_manager.Wait();
549 
550   // Answer the dialog.
551   dialog_manager.Run(true, base::string16());
552   EXPECT_TRUE(WaitForLoadStop(wc));
553 
554   // The reload should have cleared the user gesture bit, so upon leaving again
555   // there should be no beforeunload dialog.
556   shell()->LoadURL(GURL("about:blank"));
557   EXPECT_TRUE(WaitForLoadStop(wc));
558 
559   wc->SetDelegate(nullptr);
560   wc->SetJavaScriptDialogManagerForTesting(nullptr);
561 }
562 
563 // Test for crbug.com/80401.  Canceling a beforeunload dialog should reset
564 // the URL to the previous page's URL.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,CancelBeforeUnloadResetsURL)565 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
566                        CancelBeforeUnloadResetsURL) {
567   WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
568   TestJavaScriptDialogManager dialog_manager;
569   wc->SetDelegate(&dialog_manager);
570 
571   GURL url(GetTestUrl("render_frame_host", "beforeunload.html"));
572   EXPECT_TRUE(NavigateToURL(shell(), url));
573   PrepContentsForBeforeUnloadTest(wc);
574 
575   // Navigate to a page that triggers a cross-site transition.
576   GURL url2(embedded_test_server()->GetURL("foo.com", "/title1.html"));
577   shell()->LoadURL(url2);
578   dialog_manager.Wait();
579 
580   // Cancel the dialog.
581   dialog_manager.reset_url_invalidate_count();
582   dialog_manager.Run(false, base::string16());
583   EXPECT_FALSE(wc->IsLoading());
584 
585   // Verify there are no pending history items after the dialog is cancelled.
586   // (see crbug.com/93858)
587   NavigationEntry* entry = wc->GetController().GetPendingEntry();
588   EXPECT_EQ(nullptr, entry);
589   EXPECT_EQ(url, wc->GetVisibleURL());
590 
591   // There should have been at least one NavigationStateChange event for
592   // invalidating the URL in the address bar, to avoid leaving the stale URL
593   // visible.
594   EXPECT_GE(dialog_manager.url_invalidate_count(), 1);
595 
596   wc->SetDelegate(nullptr);
597   wc->SetJavaScriptDialogManagerForTesting(nullptr);
598 }
599 
600 // Helper class for beforunload tests.  Sets up a custom dialog manager for the
601 // main WebContents and provides helpers to register and test beforeunload
602 // handlers.
603 //
604 // TODO(alexmos): Refactor other beforeunload tests in this file to use this
605 // class.
606 class RenderFrameHostImplBeforeUnloadBrowserTest
607     : public RenderFrameHostImplBrowserTest {
608  public:
RenderFrameHostImplBeforeUnloadBrowserTest()609   RenderFrameHostImplBeforeUnloadBrowserTest() {}
610 
web_contents()611   WebContentsImpl* web_contents() {
612     return static_cast<WebContentsImpl*>(shell()->web_contents());
613   }
614 
dialog_manager()615   TestJavaScriptDialogManager* dialog_manager() {
616     return dialog_manager_.get();
617   }
618 
CloseDialogAndProceed()619   void CloseDialogAndProceed() {
620     dialog_manager_->Run(true /* navigation should proceed */,
621                          base::string16());
622   }
623 
CloseDialogAndCancel()624   void CloseDialogAndCancel() {
625     dialog_manager_->Run(false /* navigation should proceed */,
626                          base::string16());
627   }
628 
629   // Installs a beforeunload handler in the given frame.
630   // |before_unload_options| specify whether the handler should send a "ping"
631   // message through domAutomationController, and/or whether it should trigger
632   // the modal beforeunload confirmation dialog.
633   enum BeforeUnloadOptions {
634     SHOW_DIALOG = 1,
635     SEND_PING = 2,
636   };
InstallBeforeUnloadHandler(FrameTreeNode * ftn,int before_unload_options)637   void InstallBeforeUnloadHandler(FrameTreeNode* ftn,
638                                   int before_unload_options) {
639     std::string script = "window.onbeforeunload = () => { ";
640     if (before_unload_options & SEND_PING)
641       script += "domAutomationController.send('ping'); ";
642     if (before_unload_options & SHOW_DIALOG)
643       script += "return 'x'; ";
644     script += " }";
645     EXPECT_TRUE(ExecuteScript(ftn, script));
646   }
647 
RetrievePingsFromMessageQueue(DOMMessageQueue * msg_queue)648   int RetrievePingsFromMessageQueue(DOMMessageQueue* msg_queue) {
649     int num_pings = 0;
650     std::string message;
651     while (msg_queue->PopMessage(&message)) {
652       base::TrimString(message, "\"", &message);
653       // Only count messages from beforeunload.  For example, an ExecuteScript
654       // sends its own message to DOMMessageQueue, which we need to ignore.
655       if (message == "ping")
656         ++num_pings;
657     }
658     return num_pings;
659   }
660 
661  protected:
SetUpOnMainThread()662   void SetUpOnMainThread() override {
663     RenderFrameHostImplBrowserTest::SetUpOnMainThread();
664     dialog_manager_.reset(new TestJavaScriptDialogManager);
665     web_contents()->SetDelegate(dialog_manager_.get());
666   }
667 
TearDownOnMainThread()668   void TearDownOnMainThread() override {
669     web_contents()->SetDelegate(nullptr);
670     web_contents()->SetJavaScriptDialogManagerForTesting(nullptr);
671     RenderFrameHostImplBrowserTest::TearDownOnMainThread();
672   }
673 
674  private:
675   std::unique_ptr<TestJavaScriptDialogManager> dialog_manager_;
676 
677   DISALLOW_COPY_AND_ASSIGN(RenderFrameHostImplBeforeUnloadBrowserTest);
678 };
679 
680 // Check that when a frame performs a browser-initiated navigation, its
681 // cross-site subframe is able to execute a beforeunload handler and put up a
682 // dialog to cancel or allow the navigation. This matters especially in
683 // --site-per-process mode; see https://crbug.com/853021.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBeforeUnloadBrowserTest,SubframeShowsDialogWhenMainFrameNavigates)684 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBeforeUnloadBrowserTest,
685                        SubframeShowsDialogWhenMainFrameNavigates) {
686   GURL main_url(embedded_test_server()->GetURL(
687       "a.com", "/cross_site_iframe_factory.html?a(b)"));
688   EXPECT_TRUE(NavigateToURL(shell(), main_url));
689 
690   // Install a beforeunload handler in the b.com subframe.
691   FrameTreeNode* root = web_contents()->GetFrameTree()->root();
692   InstallBeforeUnloadHandler(root->child_at(0), SHOW_DIALOG);
693 
694   // Disable beforeunload timer to prevent flakiness.
695   PrepContentsForBeforeUnloadTest(web_contents());
696 
697   // Navigate cross-site.
698   GURL cross_site_url(embedded_test_server()->GetURL("c.com", "/title1.html"));
699   shell()->LoadURL(cross_site_url);
700 
701   // Only the main frame should be marked as waiting for beforeunload completion
702   // callback as the frame being navigated.
703   RenderFrameHostImpl* main_frame = web_contents()->GetMainFrame();
704   RenderFrameHostImpl* child = root->child_at(0)->current_frame_host();
705   EXPECT_TRUE(main_frame->is_waiting_for_beforeunload_completion());
706   EXPECT_FALSE(child->is_waiting_for_beforeunload_completion());
707 
708   // Sanity check that the main frame is waiting for subframe's beforeunload
709   // ACK.
710   EXPECT_EQ(main_frame, child->GetBeforeUnloadInitiator());
711   EXPECT_EQ(main_frame, main_frame->GetBeforeUnloadInitiator());
712 
713   // When in --site-per-process mode, LoadURL() should trigger two beforeunload
714   // IPCs for subframe and the main frame: the subframe has a beforeunload
715   // handler, and while the main frame does not, we always send the IPC to
716   // navigating frames, regardless of whether or not they have a handler.
717   //
718   // Without --site-per-process, only one beforeunload IPC should be sent to
719   // the main frame, which will handle both (same-process) frames.
720   EXPECT_EQ(AreAllSitesIsolatedForTesting() ? 2u : 1u,
721             main_frame->beforeunload_pending_replies_.size());
722 
723   // Wait for the beforeunload dialog to be shown from the subframe.
724   dialog_manager()->Wait();
725 
726   // The main frame should still be waiting for subframe's beforeunload
727   // completion callback.
728   EXPECT_EQ(main_frame, child->GetBeforeUnloadInitiator());
729   EXPECT_EQ(main_frame, main_frame->GetBeforeUnloadInitiator());
730   EXPECT_TRUE(main_frame->is_waiting_for_beforeunload_completion());
731   EXPECT_FALSE(child->is_waiting_for_beforeunload_completion());
732 
733   // In --site-per-process mode, the beforeunload completion callback should
734   // happen on the child RFH.  Without --site-per-process, it will come from the
735   // main frame RFH, which processes beforeunload for both main frame and child
736   // frame, since they are in the same process.
737   RenderFrameHostImpl* frame_that_sent_beforeunload_ipc =
738       AreAllSitesIsolatedForTesting() ? child : main_frame;
739   EXPECT_TRUE(main_frame->beforeunload_pending_replies_.count(
740       frame_that_sent_beforeunload_ipc));
741 
742   // Answer the dialog with "cancel" to stay on current page.
743   CloseDialogAndCancel();
744   EXPECT_TRUE(WaitForLoadStop(web_contents()));
745   EXPECT_EQ(main_url, web_contents()->GetLastCommittedURL());
746 
747   // Verify beforeunload state has been cleared.
748   EXPECT_FALSE(main_frame->is_waiting_for_beforeunload_completion());
749   EXPECT_FALSE(child->is_waiting_for_beforeunload_completion());
750   EXPECT_EQ(nullptr, main_frame->GetBeforeUnloadInitiator());
751   EXPECT_EQ(nullptr, child->GetBeforeUnloadInitiator());
752   EXPECT_EQ(0u, main_frame->beforeunload_pending_replies_.size());
753 
754   // Try navigating again.  The dialog should come up again.
755   shell()->LoadURL(cross_site_url);
756   dialog_manager()->Wait();
757   EXPECT_TRUE(main_frame->is_waiting_for_beforeunload_completion());
758 
759   // Now answer the dialog and allow the navigation to proceed.  Disable
760   // unload ACK on the old frame so that it sticks around in pending delete
761   // state, since the test later verifies that it has received the beforeunload
762   // ACK.
763   TestFrameNavigationObserver commit_observer(root);
764   main_frame->DisableUnloadTimerForTesting();
765   CloseDialogAndProceed();
766   commit_observer.WaitForCommit();
767   EXPECT_EQ(cross_site_url, web_contents()->GetLastCommittedURL());
768   EXPECT_FALSE(
769       web_contents()->GetMainFrame()->is_waiting_for_beforeunload_completion());
770 
771   // The navigation that succeeded was a browser-initiated, main frame
772   // navigation, so it swapped RenderFrameHosts. |main_frame| should now be
773   // pending deletion and waiting for unload ACK, but it should not be waiting
774   // for the beforeunload completion callback.
775   EXPECT_FALSE(main_frame->is_active());
776   EXPECT_FALSE(main_frame->is_waiting_for_beforeunload_completion());
777   EXPECT_EQ(0u, main_frame->beforeunload_pending_replies_.size());
778   EXPECT_EQ(nullptr, main_frame->GetBeforeUnloadInitiator());
779 }
780 
781 // Check that when a frame with multiple cross-site subframes navigates, all
782 // the subframes execute their beforeunload handlers, but at most one
783 // beforeunload dialog is allowed per navigation.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBeforeUnloadBrowserTest,MultipleSubframes)784 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBeforeUnloadBrowserTest,
785                        MultipleSubframes) {
786   GURL main_url(embedded_test_server()->GetURL(
787       "a.com", "/cross_site_iframe_factory.html?a(b(c),b,c(d),c,d)"));
788   EXPECT_TRUE(NavigateToURL(shell(), main_url));
789 
790   // Install a beforeunload handler in five of eight frames to send a ping via
791   // domAutomationController and request a beforeunload dialog.
792   FrameTreeNode* root = web_contents()->GetFrameTree()->root();
793   InstallBeforeUnloadHandler(root, SEND_PING | SHOW_DIALOG);
794   InstallBeforeUnloadHandler(root->child_at(0)->child_at(0),
795                              SEND_PING | SHOW_DIALOG);
796   InstallBeforeUnloadHandler(root->child_at(1), SEND_PING | SHOW_DIALOG);
797   InstallBeforeUnloadHandler(root->child_at(2), SEND_PING | SHOW_DIALOG);
798   InstallBeforeUnloadHandler(root->child_at(2)->child_at(0),
799                              SEND_PING | SHOW_DIALOG);
800 
801   // Disable beforeunload timer to prevent flakiness.
802   PrepContentsForBeforeUnloadTest(web_contents());
803 
804   // Navigate main frame cross-site and wait for the beforeunload dialog to be
805   // shown from one of the frames.
806   DOMMessageQueue msg_queue;
807   GURL cross_site_url(embedded_test_server()->GetURL("e.com", "/title1.html"));
808   shell()->LoadURL(cross_site_url);
809   dialog_manager()->Wait();
810 
811   // Answer the dialog and allow the navigation to proceed.
812   CloseDialogAndProceed();
813   EXPECT_TRUE(WaitForLoadStop(web_contents()));
814   EXPECT_EQ(cross_site_url, web_contents()->GetLastCommittedURL());
815 
816   // We should've received five beforeunload pings.
817   EXPECT_EQ(5, RetrievePingsFromMessageQueue(&msg_queue));
818 
819   // No more beforeunload dialogs shouldn't been shown, due to a policy of at
820   // most one dialog per navigation.
821   EXPECT_EQ(1, dialog_manager()->num_beforeunload_dialogs_seen());
822 }
823 
824 // Similar to the test above, but test scenarios where the subframes with
825 // beforeunload handlers aren't local roots.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBeforeUnloadBrowserTest,NonLocalRootSubframes)826 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBeforeUnloadBrowserTest,
827                        NonLocalRootSubframes) {
828   GURL main_url(embedded_test_server()->GetURL(
829       "a.com", "/cross_site_iframe_factory.html?a(a(b),c(c))"));
830   EXPECT_TRUE(NavigateToURL(shell(), main_url));
831 
832   // Install a beforeunload handler in two of five frames to send a ping via
833   // domAutomationController and request a beforeunload dialog.
834   FrameTreeNode* root = web_contents()->GetFrameTree()->root();
835   InstallBeforeUnloadHandler(root->child_at(0), SEND_PING | SHOW_DIALOG);
836   InstallBeforeUnloadHandler(root->child_at(0)->child_at(0),
837                              SEND_PING | SHOW_DIALOG);
838 
839   // Disable beforeunload timer to prevent flakiness.
840   PrepContentsForBeforeUnloadTest(web_contents());
841 
842   // Navigate and wait for the beforeunload dialog to be shown from one of the
843   // frames.
844   DOMMessageQueue msg_queue;
845   GURL cross_site_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
846   shell()->LoadURL(cross_site_url);
847   dialog_manager()->Wait();
848 
849   // Answer the dialog and allow the navigation to proceed.
850   CloseDialogAndProceed();
851   EXPECT_TRUE(WaitForLoadStop(web_contents()));
852   EXPECT_EQ(cross_site_url, web_contents()->GetLastCommittedURL());
853 
854   // We should've received two beforeunload pings.
855   EXPECT_EQ(2, RetrievePingsFromMessageQueue(&msg_queue));
856 
857   // No more beforeunload dialogs shouldn't been shown, due to a policy of at
858   // most one dialog per navigation.
859   EXPECT_EQ(1, dialog_manager()->num_beforeunload_dialogs_seen());
860 }
861 
862 // Test that cross-site subframes run the beforeunload handler when the main
863 // frame performs a renderer-initiated navigation.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBeforeUnloadBrowserTest,RendererInitiatedNavigation)864 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBeforeUnloadBrowserTest,
865                        RendererInitiatedNavigation) {
866   GURL main_url(embedded_test_server()->GetURL(
867       "a.com", "/cross_site_iframe_factory.html?a(a,b,c)"));
868   EXPECT_TRUE(NavigateToURL(shell(), main_url));
869 
870   // Install a beforeunload handler in both a.com frames to send a ping via
871   // domAutomationController.
872   FrameTreeNode* root = web_contents()->GetFrameTree()->root();
873   InstallBeforeUnloadHandler(root, SEND_PING);
874   InstallBeforeUnloadHandler(root->child_at(0), SEND_PING);
875 
876   // Install a beforeunload handler in the b.com frame to put up a dialog.
877   InstallBeforeUnloadHandler(root->child_at(1), SHOW_DIALOG);
878 
879   // Disable beforeunload timer to prevent flakiness.
880   PrepContentsForBeforeUnloadTest(web_contents());
881 
882   // Start a same-site renderer-initiated navigation.  The beforeunload dialog
883   // from the b.com frame should be shown.  The other two a.com frames should
884   // send pings from their beforeunload handlers.
885   DOMMessageQueue msg_queue;
886   GURL new_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
887   TestNavigationManager navigation_manager(web_contents(), new_url);
888   // Use ExecuteScriptAsync because a ping may arrive before the script
889   // execution completion notification and confuse our expectations.
890   ExecuteScriptAsync(root, "location.href = '" + new_url.spec() + "';");
891   dialog_manager()->Wait();
892 
893   // Answer the dialog and allow the navigation to proceed.  Note that at this
894   // point, without site isolation, the navigation hasn't started yet, as the
895   // navigating frame is still processing beforeunload for all its descendant
896   // local frames.  With site isolation, the a.com frames have finished
897   // beforeunload, and the browser process has received OnBeginNavigation, but
898   // the navigation is paused until the b.com subframe process finishes running
899   // beforeunload.
900   CloseDialogAndProceed();
901 
902   // Wait for navigation to end.
903   navigation_manager.WaitForNavigationFinished();
904   EXPECT_EQ(new_url, web_contents()->GetLastCommittedURL());
905 
906   // We should have received two pings from two a.com frames.  If we receive
907   // more, that probably means we ran beforeunload an extra time in the a.com
908   // frames.
909   EXPECT_EQ(2, RetrievePingsFromMessageQueue(&msg_queue));
910   EXPECT_EQ(1, dialog_manager()->num_beforeunload_dialogs_seen());
911 }
912 
913 // Similar to the test above, but check a navigation in a subframe rather than
914 // the main frame.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBeforeUnloadBrowserTest,RendererInitiatedNavigationInSubframe)915 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBeforeUnloadBrowserTest,
916                        RendererInitiatedNavigationInSubframe) {
917   GURL main_url(embedded_test_server()->GetURL(
918       "a.com", "/cross_site_iframe_factory.html?a(b(c),c)"));
919   EXPECT_TRUE(NavigateToURL(shell(), main_url));
920 
921   // Install a beforeunload handler to send a ping in all frames.
922   FrameTreeNode* root = web_contents()->GetFrameTree()->root();
923   InstallBeforeUnloadHandler(root, SEND_PING);
924   InstallBeforeUnloadHandler(root->child_at(0), SEND_PING);
925   InstallBeforeUnloadHandler(root->child_at(0)->child_at(0), SEND_PING);
926   InstallBeforeUnloadHandler(root->child_at(1), SEND_PING);
927 
928   // Disable beforeunload timer to prevent flakiness.
929   PrepContentsForBeforeUnloadTest(web_contents());
930 
931   // Start a renderer-initiated navigation in the middle frame.
932   DOMMessageQueue msg_queue;
933   GURL new_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
934   TestNavigationManager navigation_manager(web_contents(), new_url);
935   // Use ExecuteScriptAsync because a ping may arrive before the script
936   // execution completion notification and confuse our expectations.
937   ExecuteScriptAsync(root->child_at(0),
938                      "location.href = '" + new_url.spec() + "';");
939   navigation_manager.WaitForNavigationFinished();
940   EXPECT_EQ(new_url,
941             root->child_at(0)->current_frame_host()->GetLastCommittedURL());
942 
943   // We should have received two pings from the b.com frame and its child.
944   // Other frames' beforeunload handlers shouldn't have run.
945   EXPECT_EQ(2, RetrievePingsFromMessageQueue(&msg_queue));
946 
947   // We shouldn't have seen any beforeunload dialogs.
948   EXPECT_EQ(0, dialog_manager()->num_beforeunload_dialogs_seen());
949 }
950 
951 // Ensure that when a beforeunload handler deletes a subframe which is also
952 // running beforeunload, the navigation can still proceed.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBeforeUnloadBrowserTest,DetachSubframe)953 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBeforeUnloadBrowserTest,
954                        DetachSubframe) {
955   GURL main_url(embedded_test_server()->GetURL(
956       "a.com", "/cross_site_iframe_factory.html?a(b)"));
957   EXPECT_TRUE(NavigateToURL(shell(), main_url));
958 
959   // Install a beforeunload handler in root frame to delete the subframe.
960   FrameTreeNode* root = web_contents()->GetFrameTree()->root();
961   std::string script =
962       "window.onbeforeunload = () => { "
963       "  document.body.removeChild(document.querySelector('iframe'));"
964       "}";
965   EXPECT_TRUE(ExecuteScript(root, script));
966 
967   // Install a beforeunload handler which never finishes in subframe.
968   EXPECT_TRUE(ExecuteScript(root->child_at(0),
969                             "window.onbeforeunload = () => { while (1) ; }"));
970 
971   // Disable beforeunload timer to prevent flakiness.
972   PrepContentsForBeforeUnloadTest(web_contents());
973 
974   // Navigate main frame and ensure that it doesn't time out.  When the main
975   // frame detaches the subframe, the RFHI destruction should unblock the
976   // navigation from waiting on the subframe's beforeunload completion callback.
977   GURL new_url(embedded_test_server()->GetURL("c.com", "/title1.html"));
978   EXPECT_TRUE(NavigateToURL(shell(), new_url));
979 }
980 
981 // Ensure that A(B(A)) cases work sanely with beforeunload handlers.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBeforeUnloadBrowserTest,RendererInitiatedNavigationInABAB)982 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBeforeUnloadBrowserTest,
983                        RendererInitiatedNavigationInABAB) {
984   GURL main_url(embedded_test_server()->GetURL(
985       "a.com", "/cross_site_iframe_factory.html?a(b(a(b)))"));
986   EXPECT_TRUE(NavigateToURL(shell(), main_url));
987 
988   // Install a beforeunload handler to send a ping in all frames.
989   FrameTreeNode* root = web_contents()->GetFrameTree()->root();
990   InstallBeforeUnloadHandler(root, SEND_PING);
991   InstallBeforeUnloadHandler(root->child_at(0), SEND_PING);
992   InstallBeforeUnloadHandler(root->child_at(0)->child_at(0), SEND_PING);
993   InstallBeforeUnloadHandler(root->child_at(0)->child_at(0)->child_at(0),
994                              SEND_PING);
995 
996   // Disable beforeunload timer to prevent flakiness.
997   PrepContentsForBeforeUnloadTest(web_contents());
998 
999   // Navigate the main frame.
1000   DOMMessageQueue msg_queue;
1001   GURL new_url(embedded_test_server()->GetURL("c.com", "/title1.html"));
1002   EXPECT_TRUE(NavigateToURL(shell(), new_url));
1003 
1004   // We should have received four pings.
1005   EXPECT_EQ(4, RetrievePingsFromMessageQueue(&msg_queue));
1006 
1007   // We shouldn't have seen any beforeunload dialogs.
1008   EXPECT_EQ(0, dialog_manager()->num_beforeunload_dialogs_seen());
1009 }
1010 
1011 // Ensure that the beforeunload timeout works properly when
1012 // beforeunload handlers from subframes time out.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBeforeUnloadBrowserTest,TimeoutInSubframe)1013 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBeforeUnloadBrowserTest,
1014                        TimeoutInSubframe) {
1015   GURL main_url(embedded_test_server()->GetURL(
1016       "a.com", "/cross_site_iframe_factory.html?a(b)"));
1017   EXPECT_TRUE(NavigateToURL(shell(), main_url));
1018 
1019   // Install a beforeunload handler to send a ping in main frame.
1020   FrameTreeNode* root = web_contents()->GetFrameTree()->root();
1021   InstallBeforeUnloadHandler(root, SEND_PING);
1022 
1023   // Install a beforeunload handler which never finishes in subframe.
1024   EXPECT_TRUE(ExecuteScript(root->child_at(0),
1025                             "window.onbeforeunload = () => { while (1) ; }"));
1026 
1027   // Navigate the main frame.  We should eventually time out on the subframe
1028   // beforeunload handler and complete the navigation.
1029   GURL new_url(embedded_test_server()->GetURL("c.com", "/title1.html"));
1030   EXPECT_TRUE(NavigateToURL(shell(), new_url));
1031 }
1032 
1033 // Ensure that the beforeunload timeout isn't restarted when a frame attempts
1034 // to show a beforeunload dialog and fails because the dialog is already being
1035 // shown by another frame.  See https://crbug.com/865223.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBeforeUnloadBrowserTest,TimerNotRestartedBySecondDialog)1036 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBeforeUnloadBrowserTest,
1037                        TimerNotRestartedBySecondDialog) {
1038   // This test exercises a scenario that's only possible with
1039   // --site-per-process.
1040   if (!AreAllSitesIsolatedForTesting())
1041     return;
1042 
1043   GURL main_url(embedded_test_server()->GetURL(
1044       "a.com", "/cross_site_iframe_factory.html?a(b)"));
1045   EXPECT_TRUE(NavigateToURL(shell(), main_url));
1046   RenderFrameHostImpl* main_frame = web_contents()->GetMainFrame();
1047 
1048   // Install a beforeunload handler to show a dialog in both frames.
1049   FrameTreeNode* root = web_contents()->GetFrameTree()->root();
1050   InstallBeforeUnloadHandler(root, SHOW_DIALOG);
1051   InstallBeforeUnloadHandler(root->child_at(0), SHOW_DIALOG);
1052 
1053   // Extend the beforeunload timeout to prevent flakiness.  This test can't use
1054   // PrepContentsForBeforeUnloadTest(), as that clears the timer altogether,
1055   // and this test needs the timer to be valid, to see whether it gets paused
1056   // and not restarted correctly.
1057   main_frame->SetBeforeUnloadTimeoutDelayForTesting(
1058       base::TimeDelta::FromSeconds(30));
1059 
1060   // Start a navigation in the main frame.
1061   GURL new_url(embedded_test_server()->GetURL("c.com", "/title1.html"));
1062   shell()->LoadURL(new_url);
1063 
1064   // We should have two pending beforeunload completion callbacks at this point,
1065   // and the beforeunload timer should be running.
1066   EXPECT_EQ(2u, main_frame->beforeunload_pending_replies_.size());
1067   EXPECT_TRUE(main_frame->beforeunload_timeout_->IsRunning());
1068 
1069   // Wait for the dialog from one of the frames.  Note that either frame could
1070   // be the first to trigger the dialog.
1071   dialog_manager()->Wait();
1072 
1073   // The dialog should've canceled the timer.
1074   EXPECT_FALSE(main_frame->beforeunload_timeout_->IsRunning());
1075 
1076   // Don't close the dialog and allow the second beforeunload to come in and
1077   // attempt to show a dialog.  This should fail due to the intervention of at
1078   // most one dialog per navigation and respond to the renderer with the
1079   // confirmation to proceed, which should trigger a beforeunload completion
1080   // callback from the second frame. Wait for that beforeunload completion
1081   // callback. After it's received, there will be one ACK remaining for the
1082   // frame that's currently showing the dialog.
1083   while (main_frame->beforeunload_pending_replies_.size() > 1) {
1084     base::RunLoop run_loop;
1085     base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
1086         FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout());
1087     run_loop.Run();
1088   }
1089 
1090   // Ensure that the beforeunload timer hasn't been restarted, since the first
1091   // beforeunload dialog is still up at this point.
1092   EXPECT_FALSE(main_frame->beforeunload_timeout_->IsRunning());
1093 
1094   // Cancel the dialog and make sure we stay on the old page.
1095   CloseDialogAndCancel();
1096   EXPECT_TRUE(WaitForLoadStop(web_contents()));
1097   EXPECT_EQ(main_url, web_contents()->GetLastCommittedURL());
1098 }
1099 
1100 namespace {
1101 
1102 // A helper to execute some script in a frame just before it is deleted, such
1103 // that no message loops are pumped and no sync IPC messages are processed
1104 // between script execution and the destruction of the RenderFrameHost  .
1105 class ExecuteScriptBeforeRenderFrameDeletedHelper
1106     : public RenderFrameDeletedObserver {
1107  public:
ExecuteScriptBeforeRenderFrameDeletedHelper(RenderFrameHost * observed_frame,const std::string & script)1108   ExecuteScriptBeforeRenderFrameDeletedHelper(RenderFrameHost* observed_frame,
1109                                               const std::string& script)
1110       : RenderFrameDeletedObserver(observed_frame), script_(script) {}
1111 
1112  protected:
1113   // WebContentsObserver:
RenderFrameDeleted(RenderFrameHost * render_frame_host)1114   void RenderFrameDeleted(RenderFrameHost* render_frame_host) override {
1115     const bool was_deleted = deleted();
1116     RenderFrameDeletedObserver::RenderFrameDeleted(render_frame_host);
1117     if (deleted() && !was_deleted)
1118       ExecuteScriptAsync(render_frame_host, script_);
1119   }
1120 
1121  private:
1122   std::string script_;
1123 
1124   DISALLOW_COPY_AND_ASSIGN(ExecuteScriptBeforeRenderFrameDeletedHelper);
1125 };
1126 
1127 }  // namespace
1128 
1129 // Regression test for https://crbug.com/728171 where the sync IPC channel has a
1130 // connection error but we don't properly check for it. This occurs because we
1131 // send a sync window.open IPC after the RenderFrameHost is destroyed.
1132 //
1133 // The test creates two WebContents rendered in the same process. The first is
1134 // is the window-opener of the second, so the first window can be used to relay
1135 // information collected during the destruction of the RenderFrame in the second
1136 // WebContents back to the browser process.
1137 //
1138 // The issue is then reproduced by asynchronously triggering a call to
1139 // window.open() in the main frame of the second WebContents in response to
1140 // WebContentsObserver::RenderFrameDeleted -- that is, just before the RFHI is
1141 // destroyed on the browser side. The test assumes that between these two
1142 // events, the UI message loop is not pumped, and no sync IPC messages are
1143 // processed on the UI thread.
1144 //
1145 // Note that if the second WebContents scheduled a call to window.close() to
1146 // close itself after it calls window.open(), the CreateNewWindow sync IPC could
1147 // be dispatched *before* WidgetHostMsg_Close in the browser process, provided
1148 // that the browser happened to be in IPC::SyncChannel::WaitForReply on the UI
1149 // thread (most likely after sending GpuCommandBufferMsg_* messages), in which
1150 // case incoming sync IPCs to this thread are dispatched, but the message loop
1151 // is not pumped, so proxied non-sync IPCs are not delivered.
1152 //
1153 // Furthermore, on Android, exercising window.open() must be delayed until after
1154 // content::RemoveShellView returns, as that method calls into JNI to close the
1155 // view corresponding to the WebContents, which will then call back into native
1156 // code and may run nested message loops and send sync IPC messages.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,FrameDetached_WindowOpenIPCFails)1157 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
1158                        FrameDetached_WindowOpenIPCFails) {
1159   EXPECT_TRUE(NavigateToURL(shell(), GetTestUrl("", "title1.html")));
1160   EXPECT_EQ(1u, Shell::windows().size());
1161   GURL test_url = GetTestUrl("render_frame_host", "window_open.html");
1162   std::string open_script =
1163       base::StringPrintf("popup = window.open('%s');", test_url.spec().c_str());
1164 
1165   TestNavigationObserver second_contents_navigation_observer(nullptr, 1);
1166   second_contents_navigation_observer.StartWatchingNewWebContents();
1167   EXPECT_TRUE(content::ExecuteScript(shell(), open_script));
1168   second_contents_navigation_observer.Wait();
1169 
1170   ASSERT_EQ(2u, Shell::windows().size());
1171   Shell* new_shell = Shell::windows()[1];
1172   ExecuteScriptBeforeRenderFrameDeletedHelper deleted_observer(
1173       new_shell->web_contents()->GetMainFrame(), "callWindowOpen();");
1174   new_shell->Close();
1175   deleted_observer.WaitUntilDeleted();
1176 
1177   bool did_call_window_open = false;
1178   EXPECT_TRUE(ExecuteScriptAndExtractBool(
1179       shell(), "domAutomationController.send(!!popup.didCallWindowOpen)",
1180       &did_call_window_open));
1181   EXPECT_TRUE(did_call_window_open);
1182 
1183   std::string result_of_window_open;
1184   EXPECT_TRUE(ExecuteScriptAndExtractString(
1185       shell(), "domAutomationController.send(String(popup.resultOfWindowOpen))",
1186       &result_of_window_open));
1187   EXPECT_EQ("null", result_of_window_open);
1188 }
1189 
1190 namespace {
PostRequestMonitor(int * post_counter,const net::test_server::HttpRequest & request)1191 void PostRequestMonitor(int* post_counter,
1192                         const net::test_server::HttpRequest& request) {
1193   if (request.method != net::test_server::METHOD_POST)
1194     return;
1195   (*post_counter)++;
1196   auto it = request.headers.find("Content-Type");
1197   CHECK(it != request.headers.end());
1198   CHECK(!it->second.empty());
1199 }
1200 }  // namespace
1201 
1202 // Verifies form submits and resubmits work.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,POSTNavigation)1203 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest, POSTNavigation) {
1204   net::EmbeddedTestServer http_server;
1205   http_server.AddDefaultHandlers(GetTestDataFilePath());
1206   int post_counter = 0;
1207   http_server.RegisterRequestMonitor(
1208       base::BindRepeating(&PostRequestMonitor, &post_counter));
1209   ASSERT_TRUE(http_server.Start());
1210 
1211   GURL url(http_server.GetURL("/session_history/form.html"));
1212   GURL post_url = http_server.GetURL("/echotitle");
1213 
1214   // Navigate to a page with a form.
1215   TestNavigationObserver observer(shell()->web_contents());
1216   EXPECT_TRUE(NavigateToURL(shell(), url));
1217   EXPECT_EQ(url, observer.last_navigation_url());
1218   EXPECT_TRUE(observer.last_navigation_succeeded());
1219 
1220   // Submit the form.
1221   GURL submit_url("javascript:submitForm('isubmit')");
1222   EXPECT_TRUE(
1223       NavigateToURL(shell(), submit_url, post_url /* expected_commit_url */));
1224 
1225   // Check that a proper POST navigation was done.
1226   EXPECT_EQ("text=&select=a",
1227             base::UTF16ToASCII(shell()->web_contents()->GetTitle()));
1228   EXPECT_EQ(post_url, shell()->web_contents()->GetLastCommittedURL());
1229   EXPECT_TRUE(shell()
1230                   ->web_contents()
1231                   ->GetController()
1232                   .GetLastCommittedEntry()
1233                   ->GetHasPostData());
1234 
1235   // Reload and verify the form was submitted.
1236   shell()->web_contents()->GetController().Reload(ReloadType::NORMAL, false);
1237   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
1238   EXPECT_EQ("text=&select=a",
1239             base::UTF16ToASCII(shell()->web_contents()->GetTitle()));
1240   CHECK_EQ(2, post_counter);
1241 }
1242 
1243 namespace {
1244 
1245 class NavigationHandleGrabber : public WebContentsObserver {
1246  public:
NavigationHandleGrabber(WebContents * web_contents)1247   explicit NavigationHandleGrabber(WebContents* web_contents)
1248       : WebContentsObserver(web_contents) {}
1249 
ReadyToCommitNavigation(NavigationHandle * navigation_handle)1250   void ReadyToCommitNavigation(NavigationHandle* navigation_handle) override {
1251     if (navigation_handle->GetURL().path() != "/title2.html")
1252       return;
1253     NavigationRequest::From(navigation_handle)
1254         ->set_complete_callback_for_testing(
1255             base::BindOnce(&NavigationHandleGrabber::SendingNavigationCommitted,
1256                            base::Unretained(this), navigation_handle));
1257   }
1258 
SendingNavigationCommitted(NavigationHandle * navigation_handle,NavigationThrottle::ThrottleCheckResult result)1259   bool SendingNavigationCommitted(
1260       NavigationHandle* navigation_handle,
1261       NavigationThrottle::ThrottleCheckResult result) {
1262     if (navigation_handle->GetURL().path() == "/title2.html")
1263       ExecuteScriptAsync(web_contents(), "document.open();");
1264     return false;
1265   }
1266 
DidFinishNavigation(NavigationHandle * navigation_handle)1267   void DidFinishNavigation(NavigationHandle* navigation_handle) override {
1268     if (navigation_handle->GetURL().path() != "/title2.html")
1269       return;
1270     if (navigation_handle->HasCommitted())
1271       committed_title2_ = true;
1272     run_loop_.Quit();
1273   }
1274 
WaitForTitle2()1275   void WaitForTitle2() { run_loop_.Run(); }
1276 
committed_title2()1277   bool committed_title2() { return committed_title2_; }
1278 
1279  private:
1280   bool committed_title2_ = false;
1281   base::RunLoop run_loop_;
1282 };
1283 }  // namespace
1284 
1285 // Verifies that if a frame aborts a navigation right after it starts, it is
1286 // cancelled.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,FastNavigationAbort)1287 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest, FastNavigationAbort) {
1288   GURL url(embedded_test_server()->GetURL("/title1.html"));
1289   EXPECT_TRUE(NavigateToURL(shell(), url));
1290 
1291   // Now make a navigation.
1292   NavigationHandleGrabber observer(shell()->web_contents());
1293   const base::string16 title = base::ASCIIToUTF16("done");
1294   EXPECT_TRUE(ExecuteScript(shell()->web_contents(),
1295                             "window.location.href='/title2.html'"));
1296   observer.WaitForTitle2();
1297   // Flush IPCs to make sure the renderer didn't tell us to navigate. Need to
1298   // make two round trips.
1299   EXPECT_TRUE(ExecuteScript(shell()->web_contents(), ""));
1300   EXPECT_TRUE(ExecuteScript(shell()->web_contents(), ""));
1301   EXPECT_FALSE(observer.committed_title2());
1302 }
1303 
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,TerminationDisablersClearedOnRendererCrash)1304 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
1305                        TerminationDisablersClearedOnRendererCrash) {
1306   EXPECT_TRUE(NavigateToURL(
1307       shell(), GetTestUrl("render_frame_host", "beforeunload.html")));
1308   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
1309 
1310   WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
1311   RenderFrameHostImpl* main_rfh1 =
1312       static_cast<RenderFrameHostImpl*>(wc->GetMainFrame());
1313 
1314   EXPECT_TRUE(main_rfh1->GetSuddenTerminationDisablerState(
1315       blink::mojom::SuddenTerminationDisablerType::kBeforeUnloadHandler));
1316 
1317   // Make the renderer crash.
1318   RenderProcessHost* renderer_process = main_rfh1->GetProcess();
1319   RenderProcessHostWatcher crash_observer(
1320       renderer_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
1321   renderer_process->Shutdown(0);
1322   crash_observer.Wait();
1323 
1324   EXPECT_FALSE(main_rfh1->GetSuddenTerminationDisablerState(
1325       blink::mojom::SuddenTerminationDisablerType::kBeforeUnloadHandler));
1326 
1327   // This should not trigger a DCHECK once the renderer sends up the termination
1328   // disabler flags.
1329   shell()->web_contents()->GetController().Reload(ReloadType::NORMAL, false);
1330   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
1331 
1332   RenderFrameHostImpl* main_rfh2 =
1333       static_cast<RenderFrameHostImpl*>(wc->GetMainFrame());
1334   EXPECT_TRUE(main_rfh2->GetSuddenTerminationDisablerState(
1335       blink::mojom::SuddenTerminationDisablerType::kBeforeUnloadHandler));
1336 }
1337 
1338 // Aborted renderer-initiated navigations that don't destroy the current
1339 // document (e.g. no error page is displayed) must not cancel pending
1340 // XMLHttpRequests.
1341 // See https://crbug.com/762945.
IN_PROC_BROWSER_TEST_F(ContentBrowserTest,AbortedRendererInitiatedNavigationDoNotCancelPendingXHR)1342 IN_PROC_BROWSER_TEST_F(
1343     ContentBrowserTest,
1344     AbortedRendererInitiatedNavigationDoNotCancelPendingXHR) {
1345   net::test_server::ControllableHttpResponse xhr_response(
1346       embedded_test_server(), "/xhr_request");
1347   EXPECT_TRUE(embedded_test_server()->Start());
1348 
1349   GURL main_url(embedded_test_server()->GetURL("/title1.html"));
1350   EXPECT_TRUE(NavigateToURL(shell(), main_url));
1351   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
1352 
1353   // 1) Send an xhr request, but do not send its response for the moment.
1354   const char* send_slow_xhr =
1355       "var request = new XMLHttpRequest();"
1356       "request.addEventListener('abort', () => document.title = 'xhr aborted');"
1357       "request.addEventListener('load', () => document.title = 'xhr loaded');"
1358       "request.open('GET', '%s');"
1359       "request.send();";
1360   const GURL slow_url = embedded_test_server()->GetURL("/xhr_request");
1361   EXPECT_TRUE(content::ExecuteScript(
1362       shell(), base::StringPrintf(send_slow_xhr, slow_url.spec().c_str())));
1363   xhr_response.WaitForRequest();
1364 
1365   // 2) In the meantime, create a renderer-initiated navigation. It will be
1366   // aborted.
1367   TestNavigationManager observer(shell()->web_contents(),
1368                                  GURL("customprotocol:aborted"));
1369   EXPECT_TRUE(content::ExecuteScript(
1370       shell(), "window.location = 'customprotocol:aborted'"));
1371   EXPECT_FALSE(observer.WaitForResponse());
1372   observer.WaitForNavigationFinished();
1373 
1374   // 3) Send the response for the XHR requests.
1375   xhr_response.Send(
1376       "HTTP/1.1 200 OK\r\n"
1377       "Connection: close\r\n"
1378       "Content-Length: 2\r\n"
1379       "Content-Type: text/plain; charset=utf-8\r\n"
1380       "\r\n"
1381       "OK");
1382   xhr_response.Done();
1383 
1384   // 4) Wait for the XHR request to complete.
1385   const base::string16 xhr_aborted_title = base::ASCIIToUTF16("xhr aborted");
1386   const base::string16 xhr_loaded_title = base::ASCIIToUTF16("xhr loaded");
1387   TitleWatcher watcher(shell()->web_contents(), xhr_loaded_title);
1388   watcher.AlsoWaitForTitle(xhr_aborted_title);
1389 
1390   EXPECT_EQ(xhr_loaded_title, watcher.WaitAndGetTitle());
1391 }
1392 
1393 // A browser-initiated javascript-url navigation must not prevent the current
1394 // document from loading.
1395 // See https://crbug.com/766149.
IN_PROC_BROWSER_TEST_F(ContentBrowserTest,BrowserInitiatedJavascriptUrlDoNotPreventLoading)1396 IN_PROC_BROWSER_TEST_F(ContentBrowserTest,
1397                        BrowserInitiatedJavascriptUrlDoNotPreventLoading) {
1398   net::test_server::ControllableHttpResponse main_document_response(
1399       embedded_test_server(), "/main_document");
1400   EXPECT_TRUE(embedded_test_server()->Start());
1401 
1402   GURL main_document_url(embedded_test_server()->GetURL("/main_document"));
1403   TestNavigationManager main_document_observer(shell()->web_contents(),
1404                                                main_document_url);
1405 
1406   // 1) Navigate. Send the header but not the body. The navigation commits in
1407   //    the browser. The renderer is still loading the document.
1408   {
1409     shell()->LoadURL(main_document_url);
1410     EXPECT_TRUE(main_document_observer.WaitForRequestStart());
1411     main_document_observer.ResumeNavigation();  // Send the request.
1412 
1413     main_document_response.WaitForRequest();
1414     main_document_response.Send(
1415         "HTTP/1.1 200 OK\r\n"
1416         "Connection: close\r\n"
1417         "Content-Type: text/html; charset=utf-8\r\n"
1418         "\r\n");
1419 
1420     EXPECT_TRUE(main_document_observer.WaitForResponse());
1421     main_document_observer.ResumeNavigation();  // Commit the navigation.
1422   }
1423 
1424   // 2) A browser-initiated javascript-url navigation happens.
1425   {
1426     GURL javascript_url(
1427         "javascript:window.domAutomationController.send('done')");
1428     shell()->LoadURL(javascript_url);
1429     DOMMessageQueue dom_message_queue(WebContents::FromRenderFrameHost(
1430         shell()->web_contents()->GetMainFrame()));
1431     std::string done;
1432     EXPECT_TRUE(dom_message_queue.WaitForMessage(&done));
1433     EXPECT_EQ("\"done\"", done);
1434   }
1435 
1436   // 3) The end of the response is issued. The renderer must be able to receive
1437   //    it.
1438   {
1439     const base::string16 document_loaded_title =
1440         base::ASCIIToUTF16("document loaded");
1441     TitleWatcher watcher(shell()->web_contents(), document_loaded_title);
1442     main_document_response.Send(
1443         "<script>"
1444         "   window.onload = function(){"
1445         "     document.title = 'document loaded'"
1446         "   }"
1447         "</script>");
1448     main_document_response.Done();
1449     EXPECT_EQ(document_loaded_title, watcher.WaitAndGetTitle());
1450   }
1451 }
1452 
1453 // Test that a same-document browser-initiated navigation doesn't prevent a
1454 // document from loading. See https://crbug.com/769645.
IN_PROC_BROWSER_TEST_F(ContentBrowserTest,SameDocumentBrowserInitiatedNavigationWhileDocumentIsLoading)1455 IN_PROC_BROWSER_TEST_F(
1456     ContentBrowserTest,
1457     SameDocumentBrowserInitiatedNavigationWhileDocumentIsLoading) {
1458   net::test_server::ControllableHttpResponse response(embedded_test_server(),
1459                                                       "/main_document");
1460   EXPECT_TRUE(embedded_test_server()->Start());
1461 
1462   // 1) Load a new document. It reaches the ReadyToCommit stage and then is slow
1463   //    to load.
1464   GURL url(embedded_test_server()->GetURL("/main_document"));
1465   TestNavigationManager observer_new_document(shell()->web_contents(), url);
1466   shell()->LoadURL(url);
1467 
1468   // The navigation starts
1469   EXPECT_TRUE(observer_new_document.WaitForRequestStart());
1470   observer_new_document.ResumeNavigation();
1471 
1472   // The server sends the first part of the response and waits.
1473   response.WaitForRequest();
1474   response.Send(
1475       "HTTP/1.1 200 OK\r\n"
1476       "Content-Type: text/html; charset=utf-8\r\n"
1477       "\r\n"
1478       "<html>"
1479       "  <body>"
1480       "    <div id=\"anchor\"></div>"
1481       "    <script>"
1482       "      domAutomationController.send('First part received')"
1483       "    </script>");
1484 
1485   // The browser reaches the ReadyToCommit stage.
1486   EXPECT_TRUE(observer_new_document.WaitForResponse());
1487   RenderFrameHostImpl* main_rfh = static_cast<RenderFrameHostImpl*>(
1488       shell()->web_contents()->GetMainFrame());
1489   DOMMessageQueue dom_message_queue(WebContents::FromRenderFrameHost(main_rfh));
1490   observer_new_document.ResumeNavigation();
1491 
1492   // Wait for the renderer to load the first part of the response.
1493   std::string first_part_received;
1494   EXPECT_TRUE(dom_message_queue.WaitForMessage(&first_part_received));
1495   EXPECT_EQ("\"First part received\"", first_part_received);
1496 
1497   // 2) In the meantime, a browser-initiated same-document navigation commits.
1498   GURL anchor_url(url.spec() + "#anchor");
1499   TestNavigationManager observer_same_document(shell()->web_contents(),
1500                                                anchor_url);
1501   shell()->LoadURL(anchor_url);
1502   observer_same_document.WaitForNavigationFinished();
1503 
1504   // 3) The last part of the response is received.
1505   response.Send(
1506       "    <script>"
1507       "      domAutomationController.send('Second part received')"
1508       "    </script>"
1509       "  </body>"
1510       "</html>");
1511   response.Done();
1512   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
1513 
1514   // The renderer should be able to load the end of the response.
1515   std::string second_part_received;
1516   EXPECT_TRUE(dom_message_queue.WaitForMessage(&second_part_received));
1517   EXPECT_EQ("\"Second part received\"", second_part_received);
1518 }
1519 
1520 namespace {
1521 
1522 // Allows injecting a fake, test-provided |interface_broker_receiver| into
1523 // DidCommitProvisionalLoad messages in a given |web_contents| instead of the
1524 // real one coming from the renderer process.
1525 class ScopedFakeInterfaceBrokerRequestInjector
1526     : public DidCommitNavigationInterceptor {
1527  public:
ScopedFakeInterfaceBrokerRequestInjector(WebContents * web_contents)1528   explicit ScopedFakeInterfaceBrokerRequestInjector(WebContents* web_contents)
1529       : DidCommitNavigationInterceptor(web_contents) {}
1530   ~ScopedFakeInterfaceBrokerRequestInjector() override = default;
1531   ScopedFakeInterfaceBrokerRequestInjector(
1532       const ScopedFakeInterfaceBrokerRequestInjector&) = delete;
1533   ScopedFakeInterfaceBrokerRequestInjector& operator=(
1534       const ScopedFakeInterfaceBrokerRequestInjector&) = delete;
1535 
1536   // Sets the fake BrowserInterfaceBroker |receiver| to inject into the next
1537   // incoming DidCommitProvisionalLoad message.
set_fake_receiver_for_next_commit(mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker> receiver)1538   void set_fake_receiver_for_next_commit(
1539       mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker> receiver) {
1540     next_fake_receiver_ = std::move(receiver);
1541   }
1542 
url_of_last_commit() const1543   const GURL& url_of_last_commit() const { return url_of_last_commit_; }
1544 
1545   const mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>&
original_receiver_of_last_commit() const1546   original_receiver_of_last_commit() const {
1547     return original_receiver_of_last_commit_;
1548   }
1549 
1550  protected:
WillProcessDidCommitNavigation(RenderFrameHost * render_frame_host,NavigationRequest * navigation_request,::FrameHostMsg_DidCommitProvisionalLoad_Params * params,mojom::DidCommitProvisionalLoadInterfaceParamsPtr * interface_params)1551   bool WillProcessDidCommitNavigation(
1552       RenderFrameHost* render_frame_host,
1553       NavigationRequest* navigation_request,
1554       ::FrameHostMsg_DidCommitProvisionalLoad_Params* params,
1555       mojom::DidCommitProvisionalLoadInterfaceParamsPtr* interface_params)
1556       override {
1557     url_of_last_commit_ = params->url;
1558     if (*interface_params) {
1559       original_receiver_of_last_commit_ =
1560           std::move((*interface_params)->browser_interface_broker_receiver);
1561       (*interface_params)->browser_interface_broker_receiver =
1562           std::move(next_fake_receiver_);
1563     }
1564     return true;
1565   }
1566 
1567  private:
1568   mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
1569       next_fake_receiver_;
1570   mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
1571       original_receiver_of_last_commit_;
1572   GURL url_of_last_commit_;
1573 };
1574 
1575 // Monitors the |broker_receiver_| of the given |render_frame_host| for incoming
1576 // interface requests for |interface_name|, and invokes |callback| synchronously
1577 // just before such a request would be dispatched.
1578 class ScopedInterfaceRequestMonitor
1579     : public blink::mojom::BrowserInterfaceBrokerInterceptorForTesting {
1580  public:
ScopedInterfaceRequestMonitor(RenderFrameHost * render_frame_host,base::StringPiece interface_name,base::RepeatingClosure callback)1581   ScopedInterfaceRequestMonitor(RenderFrameHost* render_frame_host,
1582                                 base::StringPiece interface_name,
1583                                 base::RepeatingClosure callback)
1584       : rfhi_(static_cast<RenderFrameHostImpl*>(render_frame_host)),
1585         impl_(receiver().SwapImplForTesting(this)),
1586         interface_name_(interface_name),
1587         request_callback_(callback) {}
1588 
~ScopedInterfaceRequestMonitor()1589   ~ScopedInterfaceRequestMonitor() override {
1590     auto* old_impl = receiver().SwapImplForTesting(impl_);
1591     DCHECK_EQ(old_impl, this);
1592   }
1593 
1594  protected:
1595   // blink::mojom::BrowserInterfaceBrokerInterceptorForTesting:
GetForwardingInterface()1596   blink::mojom::BrowserInterfaceBroker* GetForwardingInterface() override {
1597     return impl_;
1598   }
1599 
GetInterface(mojo::GenericPendingReceiver receiver)1600   void GetInterface(mojo::GenericPendingReceiver receiver) override {
1601     if (receiver.interface_name() == interface_name_)
1602       request_callback_.Run();
1603     GetForwardingInterface()->GetInterface(std::move(receiver));
1604   }
1605 
1606  private:
receiver()1607   mojo::Receiver<blink::mojom::BrowserInterfaceBroker>& receiver() {
1608     return rfhi_->browser_interface_broker_receiver_for_testing();
1609   }
1610 
1611   RenderFrameHostImpl* rfhi_;
1612   blink::mojom::BrowserInterfaceBroker* impl_;
1613 
1614   std::string interface_name_;
1615   base::RepeatingClosure request_callback_;
1616 
1617   DISALLOW_COPY_AND_ASSIGN(ScopedInterfaceRequestMonitor);
1618 };
1619 
1620 // Calls |callback| whenever a navigation finishes in |render_frame_host|.
1621 class DidFinishNavigationObserver : public WebContentsObserver {
1622  public:
DidFinishNavigationObserver(RenderFrameHost * render_frame_host,base::RepeatingClosure callback)1623   DidFinishNavigationObserver(RenderFrameHost* render_frame_host,
1624                               base::RepeatingClosure callback)
1625       : WebContentsObserver(
1626             WebContents::FromRenderFrameHost(render_frame_host)),
1627         callback_(callback) {}
1628 
1629  protected:
1630   // WebContentsObserver:
DidFinishNavigation(NavigationHandle * navigation_handle)1631   void DidFinishNavigation(NavigationHandle* navigation_handle) override {
1632     callback_.Run();
1633   }
1634 
1635  private:
1636   base::RepeatingClosure callback_;
1637   DISALLOW_COPY_AND_ASSIGN(DidFinishNavigationObserver);
1638 };
1639 
1640 }  // namespace
1641 
1642 // For cross-document navigations, the DidCommitProvisionalLoad message from
1643 // the renderer process will have its |interface_broker_receiver| argument set
1644 // to the receiver end of a new BrowserInterfaceBroker interface connection that
1645 // will be used by the newly committed document to access services exposed by
1646 // the RenderFrameHost.
1647 //
1648 // This test verifies that even if that |interface_broker_receiver| already
1649 // has pending interface receivers, the RenderFrameHost binds the
1650 // BrowserInterfaceBroker receiver in such a way that these pending interface
1651 // receivers are dispatched strictly after
1652 // WebContentsObserver::DidFinishNavigation has fired, so that the receivers
1653 // will be served correctly in the security context of the newly committed
1654 // document (i.e. GetLastCommittedURL/Origin will have been updated).
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,EarlyInterfaceRequestsFromNewDocumentDispatchedAfterNavigationFinished)1655 IN_PROC_BROWSER_TEST_F(
1656     RenderFrameHostImplBrowserTest,
1657     EarlyInterfaceRequestsFromNewDocumentDispatchedAfterNavigationFinished) {
1658   const GURL first_url(embedded_test_server()->GetURL("/title1.html"));
1659   const GURL second_url(embedded_test_server()->GetURL("/title2.html"));
1660 
1661   // Load a URL that maps to the same SiteInstance as the second URL, to make
1662   // sure the second navigation will not be cross-process.
1663   ASSERT_TRUE(NavigateToURL(shell(), first_url));
1664 
1665   // Prepare an PendingReceiver<BrowserInterfaceBroker> with pending interface
1666   // requests.
1667   mojo::Remote<blink::mojom::BrowserInterfaceBroker>
1668       interface_broker_with_pending_requests;
1669   mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
1670       interface_broker_receiver_with_pending_receiver =
1671           interface_broker_with_pending_requests.BindNewPipeAndPassReceiver();
1672   mojo::Remote<mojom::FrameHostTestInterface> test_interface;
1673   interface_broker_with_pending_requests->GetInterface(
1674       test_interface.BindNewPipeAndPassReceiver());
1675 
1676   // Replace the |interface_broker_receiver| argument in the next
1677   // DidCommitProvisionalLoad message coming from the renderer with the
1678   // rigged |interface_broker_with_pending_requests| from above.
1679   ScopedFakeInterfaceBrokerRequestInjector injector(shell()->web_contents());
1680   injector.set_fake_receiver_for_next_commit(
1681       std::move(interface_broker_receiver_with_pending_receiver));
1682 
1683   // Expect that by the time the interface request for FrameHostTestInterface is
1684   // dispatched to the RenderFrameHost, WebContentsObserver::DidFinishNavigation
1685   // will have already been invoked.
1686   bool did_finish_navigation = false;
1687   auto* main_rfh = shell()->web_contents()->GetMainFrame();
1688   DidFinishNavigationObserver navigation_finish_observer(
1689       main_rfh, base::BindLambdaForTesting([&did_finish_navigation]() {
1690         did_finish_navigation = true;
1691       }));
1692 
1693   base::RunLoop wait_until_interface_request_is_dispatched;
1694   ScopedInterfaceRequestMonitor monitor(
1695       main_rfh, mojom::FrameHostTestInterface::Name_,
1696       base::BindLambdaForTesting([&]() {
1697         EXPECT_TRUE(did_finish_navigation);
1698         wait_until_interface_request_is_dispatched.Quit();
1699       }));
1700 
1701   // Start the same-process navigation.
1702   test::ScopedInterfaceFilterBypass filter_bypass;
1703   ASSERT_TRUE(NavigateToURL(shell(), second_url));
1704   EXPECT_EQ(main_rfh, shell()->web_contents()->GetMainFrame());
1705   EXPECT_EQ(second_url, injector.url_of_last_commit());
1706   EXPECT_TRUE(injector.original_receiver_of_last_commit().is_valid());
1707 
1708   // Wait until the interface request for FrameHostTestInterface is dispatched.
1709   wait_until_interface_request_is_dispatched.Run();
1710 }
1711 
1712 // The BrowserInterfaceBroker interface, which is used by the RenderFrame to
1713 // access Mojo services exposed by the RenderFrameHost, is not
1714 // Channel-associated, thus not synchronized with navigation IPC messages. As a
1715 // result, when the renderer commits a load, the DidCommitProvisional message
1716 // might be at race with GetInterface messages, for example, an interface
1717 // request issued by the previous document in its unload handler might arrive to
1718 // the browser process just a moment after DidCommitProvisionalLoad.
1719 //
1720 // This test verifies that even if there is such a last-second GetInterface
1721 // message originating from the previous document, it is no longer serviced.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,LateInterfaceRequestsFromOldDocumentNotDispatched)1722 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
1723                        LateInterfaceRequestsFromOldDocumentNotDispatched) {
1724   const GURL first_url(embedded_test_server()->GetURL("/title1.html"));
1725   const GURL second_url(embedded_test_server()->GetURL("/title2.html"));
1726 
1727   // Prepare an PendingReceiver<BrowserInterfaceBroker> with no pending
1728   // requests.
1729   mojo::Remote<blink::mojom::BrowserInterfaceBroker> interface_broker;
1730   mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
1731       interface_broker_receiver = interface_broker.BindNewPipeAndPassReceiver();
1732 
1733   // Set up a cunning mechanism to replace the |interface_broker_receiver|
1734   // argument in next DidCommitProvisionalLoad message with the rigged
1735   // |interface_broker_receiver| from above, whose client end is controlled by
1736   // this test; then trigger a navigation.
1737   {
1738     ScopedFakeInterfaceBrokerRequestInjector injector(shell()->web_contents());
1739     test::ScopedInterfaceFilterBypass filter_bypass;
1740     injector.set_fake_receiver_for_next_commit(
1741         std::move(interface_broker_receiver));
1742 
1743     ASSERT_TRUE(NavigateToURL(shell(), first_url));
1744     ASSERT_EQ(first_url, injector.url_of_last_commit());
1745     ASSERT_TRUE(injector.original_receiver_of_last_commit().is_valid());
1746   }
1747 
1748   // Prepare an interface receiver for FrameHostTestInterface.
1749   mojo::Remote<mojom::FrameHostTestInterface> test_interface;
1750   auto test_interface_receiver = test_interface.BindNewPipeAndPassReceiver();
1751 
1752   // Set up |dispatched_interface_request_callback| that would be invoked if the
1753   // interface receiver for FrameHostTestInterface was ever dispatched to the
1754   // RenderFrameHostImpl.
1755   base::MockCallback<base::RepeatingClosure>
1756       dispatched_interface_request_callback;
1757   auto* main_rfh = shell()->web_contents()->GetMainFrame();
1758   ScopedInterfaceRequestMonitor monitor(
1759       main_rfh, mojom::FrameHostTestInterface::Name_,
1760       dispatched_interface_request_callback.Get());
1761 
1762   // Set up the |test_interface request| to arrive on the BrowserInterfaceBroker
1763   // connection corresponding to the old document in the middle of the firing of
1764   // WebContentsObserver::DidFinishNavigation.
1765   // TODO(engedy): Should we PostTask() this instead just before synchronously
1766   // invoking DidCommitProvisionalLoad?
1767   //
1768   // Also set up |navigation_finished_callback| to be invoked afterwards, as a
1769   // sanity check to ensure that the request injection is actually executed.
1770   base::MockCallback<base::RepeatingClosure> navigation_finished_callback;
1771   DidFinishNavigationObserver navigation_finish_observer(
1772       main_rfh, base::BindLambdaForTesting([&]() {
1773         interface_broker->GetInterface(std::move(test_interface_receiver));
1774         std::move(navigation_finished_callback).Run();
1775       }));
1776 
1777   // The BrowserInterfaceBroker connection that semantically belongs to the old
1778   // document, but whose client end is actually controlled by this test, should
1779   // still be alive and well.
1780   ASSERT_TRUE(test_interface.is_bound());
1781   ASSERT_TRUE(test_interface.is_connected());
1782 
1783   base::RunLoop run_loop;
1784   test_interface.set_disconnect_handler(run_loop.QuitWhenIdleClosure());
1785 
1786   // Expect that the GetInterface message will never be dispatched, but the
1787   // DidFinishNavigation callback will be invoked.
1788   EXPECT_CALL(dispatched_interface_request_callback, Run()).Times(0);
1789   EXPECT_CALL(navigation_finished_callback, Run());
1790 
1791   // Start the same-process navigation.
1792   ASSERT_TRUE(NavigateToURL(shell(), second_url));
1793 
1794   // Wait for a connection error on the |test_interface| as a signal, after
1795   // which it can be safely assumed that no GetInterface message will ever be
1796   // dispatched from that old InterfaceConnection.
1797   run_loop.Run();
1798 
1799   EXPECT_FALSE(test_interface.is_connected());
1800 }
1801 
1802 // Test the edge case where the `window` global object asssociated with the
1803 // initial empty document is re-used for document corresponding to the first
1804 // real committed load. This happens when the security origins of the two
1805 // documents are the same. We do not want to recalculate this in the browser
1806 // process, however, so for the first commit we leave it up to the renderer
1807 // whether it wants to replace the BrowserInterfaceBroker connection or not.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,InterfaceBrokerRequestIsOptionalForFirstCommit)1808 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
1809                        InterfaceBrokerRequestIsOptionalForFirstCommit) {
1810   const GURL main_frame_url(embedded_test_server()->GetURL("/title1.html"));
1811   const GURL subframe_url(embedded_test_server()->GetURL("/title2.html"));
1812 
1813   mojo::PendingRemote<blink::mojom::BrowserInterfaceBroker> interface_broker;
1814   auto stub_interface_broker_receiver =
1815       interface_broker.InitWithNewPipeAndPassReceiver();
1816   mojo::PendingReceiver<blink::mojom::BrowserInterfaceBroker>
1817       null_interface_broker_receiver((mojo::NullReceiver()));
1818 
1819   for (auto* interface_broker_receiver :
1820        {&stub_interface_broker_receiver, &null_interface_broker_receiver}) {
1821     SCOPED_TRACE(interface_broker_receiver->is_valid());
1822 
1823     ASSERT_TRUE(NavigateToURL(shell(), main_frame_url));
1824 
1825     ScopedFakeInterfaceBrokerRequestInjector injector(shell()->web_contents());
1826     injector.set_fake_receiver_for_next_commit(
1827         std::move(*interface_broker_receiver));
1828 
1829     // Must set 'src` before adding the iframe element to the DOM, otherwise it
1830     // will load `about:blank` as the first real load instead of |subframe_url|.
1831     // See: https://crbug.com/778318.
1832     //
1833     // Note that the child frame will first cycle through loading the initial
1834     // empty document regardless of when/how/if the `src` attribute is set.
1835     const auto script = base::StringPrintf(
1836         "let f = document.createElement(\"iframe\");"
1837         "f.src=\"%s\"; "
1838         "document.body.append(f);",
1839         subframe_url.spec().c_str());
1840     ASSERT_TRUE(ExecuteScript(shell(), script));
1841 
1842     WaitForLoadStop(shell()->web_contents());
1843 
1844     FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
1845                               ->GetFrameTree()
1846                               ->root();
1847     ASSERT_EQ(1u, root->child_count());
1848     FrameTreeNode* child = root->child_at(0u);
1849 
1850     EXPECT_FALSE(injector.original_receiver_of_last_commit().is_valid());
1851     EXPECT_TRUE(child->has_committed_real_load());
1852     EXPECT_EQ(subframe_url, child->current_url());
1853   }
1854 }
1855 
1856 // Regression test for https://crbug.com/821022.
1857 //
1858 // Test the edge case of the above, namely, where the following commits take
1859 // place in a subframe embedded into a document at `http://foo.com/`:
1860 //
1861 //  1) the initial empty document (`about:blank`)
1862 //  2) `about:blank#ref`
1863 //  3) `http://foo.com`
1864 //
1865 // Here, (2) should classify as a same-document navigation, and (3) should be
1866 // considered the first real load. Because the first real load is same-origin
1867 // with the initial empty document, the latter's `window` global object
1868 // asssociated with the initial empty document is re-used for document
1869 // corresponding to the first real committed load.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,InterfaceBrokerRequestNotPresentForFirstRealLoadAfterAboutBlankWithRef)1870 IN_PROC_BROWSER_TEST_F(
1871     RenderFrameHostImplBrowserTest,
1872     InterfaceBrokerRequestNotPresentForFirstRealLoadAfterAboutBlankWithRef) {
1873   const GURL kMainFrameURL(embedded_test_server()->GetURL("/title1.html"));
1874   const GURL kSubframeURLTwo("about:blank#ref");
1875   const GURL kSubframeURLThree(embedded_test_server()->GetURL("/title2.html"));
1876   const auto kNavigateToOneThenTwoScript = base::StringPrintf(
1877       "var f = document.createElement(\"iframe\");"
1878       "f.src=\"%s\"; "
1879       "document.body.append(f);",
1880       kSubframeURLTwo.spec().c_str());
1881   const auto kNavigateToThreeScript =
1882       base::StringPrintf("f.src=\"%s\";", kSubframeURLThree.spec().c_str());
1883 
1884   ASSERT_TRUE(NavigateToURL(shell(), kMainFrameURL));
1885 
1886   // Trigger navigation (1) by creating a new subframe, and then trigger
1887   // navigation (2) by setting it's `src` attribute before adding it to the DOM.
1888   //
1889   // We must set 'src` before adding the iframe element to the DOM, otherwise it
1890   // will load `about:blank` as the first real load instead of
1891   // |kSubframeURLTwo|. See: https://crbug.com/778318.
1892   //
1893   // Note that the child frame will first cycle through loading the initial
1894   // empty document regardless of when/how/if the `src` attribute is set.
1895 
1896   ASSERT_TRUE(ExecuteScript(shell(), kNavigateToOneThenTwoScript));
1897   WaitForLoadStop(shell()->web_contents());
1898 
1899   FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
1900                             ->GetFrameTree()
1901                             ->root();
1902   ASSERT_EQ(1u, root->child_count());
1903   FrameTreeNode* child = root->child_at(0u);
1904 
1905   EXPECT_FALSE(child->has_committed_real_load());
1906   EXPECT_EQ(kSubframeURLTwo, child->current_url());
1907   EXPECT_EQ(url::Origin::Create(kMainFrameURL), child->current_origin());
1908 
1909   // Set the `src` attribute again to trigger navigation (3).
1910 
1911   TestFrameNavigationObserver commit_observer(child->current_frame_host());
1912   ScopedFakeInterfaceBrokerRequestInjector injector(shell()->web_contents());
1913   injector.set_fake_receiver_for_next_commit(mojo::NullReceiver());
1914 
1915   ASSERT_TRUE(ExecuteScript(shell(), kNavigateToThreeScript));
1916   commit_observer.WaitForCommit();
1917   EXPECT_FALSE(injector.original_receiver_of_last_commit().is_valid());
1918 
1919   EXPECT_TRUE(child->has_committed_real_load());
1920   EXPECT_EQ(kSubframeURLThree, child->current_url());
1921   EXPECT_EQ(url::Origin::Create(kMainFrameURL), child->current_origin());
1922 }
1923 
1924 namespace {
CheckURLOriginAndNetworkIsolationKey(FrameTreeNode * node,GURL url,url::Origin origin,net::NetworkIsolationKey network_isolation_key)1925 void CheckURLOriginAndNetworkIsolationKey(
1926     FrameTreeNode* node,
1927     GURL url,
1928     url::Origin origin,
1929     net::NetworkIsolationKey network_isolation_key) {
1930   EXPECT_EQ(url, node->current_url());
1931   EXPECT_EQ(origin, node->current_origin());
1932   EXPECT_EQ(network_isolation_key,
1933             node->current_frame_host()->GetNetworkIsolationKey());
1934 }
1935 }  // namespace
1936 
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,NetworkIsolationKeyInitialEmptyDocumentIframe)1937 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
1938                        NetworkIsolationKeyInitialEmptyDocumentIframe) {
1939   GURL main_frame_url(embedded_test_server()->GetURL("/title1.html"));
1940   url::Origin main_frame_origin = url::Origin::Create(main_frame_url);
1941   net::NetworkIsolationKey expected_main_frame_key =
1942       net::NetworkIsolationKey(main_frame_origin, main_frame_origin);
1943 
1944   GURL subframe_url_one("about:blank");
1945   GURL subframe_url_two("about:blank#foo");
1946   GURL subframe_url_three(
1947       embedded_test_server()->GetURL("foo.com", "/title2.html"));
1948   url::Origin subframe_origin_three = url::Origin::Create(subframe_url_three);
1949   net::NetworkIsolationKey expected_subframe_key_three =
1950       net::NetworkIsolationKey(main_frame_origin, subframe_origin_three);
1951 
1952   // Main frame navigation.
1953   ASSERT_TRUE(NavigateToURL(shell(), main_frame_url));
1954 
1955   FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
1956                             ->GetFrameTree()
1957                             ->root();
1958   CheckURLOriginAndNetworkIsolationKey(root, main_frame_url, main_frame_origin,
1959                                        expected_main_frame_key);
1960 
1961   // Create iframe.
1962   ASSERT_TRUE(ExecuteScript(shell(), R"(
1963       var f = document.createElement('iframe');
1964       f.id = 'myiframe';
1965       document.body.append(f);
1966   )"));
1967   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
1968 
1969   ASSERT_EQ(1u, root->child_count());
1970   FrameTreeNode* child = root->child_at(0u);
1971   CheckURLOriginAndNetworkIsolationKey(
1972       child, subframe_url_one, main_frame_origin, expected_main_frame_key);
1973   EXPECT_EQ(root->current_frame_host()->GetProcess(),
1974             child->current_frame_host()->GetProcess());
1975 
1976   // Same-document navigation of iframe.
1977   ASSERT_TRUE(ExecuteScript(shell(), R"(
1978       let iframe = document.querySelector('#myiframe');
1979       iframe.contentWindow.location.hash = 'foo';
1980   )"));
1981 
1982   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
1983 
1984   CheckURLOriginAndNetworkIsolationKey(
1985       child, subframe_url_two, main_frame_origin, expected_main_frame_key);
1986   EXPECT_EQ(root->current_frame_host()->GetProcess(),
1987             child->current_frame_host()->GetProcess());
1988 
1989   // Cross-document navigation of iframe.
1990   TestFrameNavigationObserver commit_observer(child->current_frame_host());
1991   std::string subframe_script_three = JsReplace(
1992       "iframe = document.querySelector('#myiframe');"
1993       "iframe.contentWindow.location.href = $1;",
1994       subframe_url_three);
1995   ASSERT_TRUE(ExecuteScript(shell(), subframe_script_three));
1996   commit_observer.WaitForCommit();
1997 
1998   CheckURLOriginAndNetworkIsolationKey(child, subframe_url_three,
1999                                        subframe_origin_three,
2000                                        expected_subframe_key_three);
2001   if (AreAllSitesIsolatedForTesting()) {
2002     EXPECT_NE(root->current_frame_host()->GetProcess(),
2003               child->current_frame_host()->GetProcess());
2004   }
2005 }
2006 
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,NetworkIsolationKeyInitialEmptyDocumentPopup)2007 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
2008                        NetworkIsolationKeyInitialEmptyDocumentPopup) {
2009   GURL main_frame_url(embedded_test_server()->GetURL("/title1.html"));
2010   url::Origin main_frame_origin = url::Origin::Create(main_frame_url);
2011   net::NetworkIsolationKey expected_main_frame_key =
2012       net::NetworkIsolationKey(main_frame_origin, main_frame_origin);
2013 
2014   GURL popup_url_one("about:blank");
2015   GURL popup_url_two("about:blank#foo");
2016   GURL popup_url_three(
2017       embedded_test_server()->GetURL("foo.com", "/title2.html"));
2018   url::Origin popup_origin_three = url::Origin::Create(popup_url_three);
2019   net::NetworkIsolationKey expected_popup_key_three =
2020       net::NetworkIsolationKey(popup_origin_three, popup_origin_three);
2021 
2022   // Main frame navigation.
2023   ASSERT_TRUE(NavigateToURL(shell(), main_frame_url));
2024 
2025   FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
2026                             ->GetFrameTree()
2027                             ->root();
2028   CheckURLOriginAndNetworkIsolationKey(root, main_frame_url, main_frame_origin,
2029                                        expected_main_frame_key);
2030 
2031   // Create popup.
2032   WebContentsAddedObserver popup_observer;
2033   ASSERT_TRUE(ExecuteScript(shell(), "var w = window.open('');"));
2034   WebContents* popup = popup_observer.GetWebContents();
2035 
2036   FrameTreeNode* popup_frame =
2037       static_cast<RenderFrameHostImpl*>(popup->GetMainFrame())
2038           ->frame_tree_node();
2039   CheckURLOriginAndNetworkIsolationKey(
2040       popup_frame, popup_url_one, main_frame_origin, expected_main_frame_key);
2041   EXPECT_EQ(root->current_frame_host()->GetProcess(),
2042             popup_frame->current_frame_host()->GetProcess());
2043 
2044   // Same-document navigation of popup.
2045   ASSERT_TRUE(ExecuteScript(shell(), "w.location.hash = 'foo';"));
2046   EXPECT_TRUE(WaitForLoadStop(popup));
2047 
2048   CheckURLOriginAndNetworkIsolationKey(
2049       popup_frame, popup_url_two, main_frame_origin, expected_main_frame_key);
2050   EXPECT_EQ(root->current_frame_host()->GetProcess(),
2051             popup_frame->current_frame_host()->GetProcess());
2052 
2053   // Cross-document navigation of popup.
2054   TestFrameNavigationObserver commit_observer(
2055       popup_frame->current_frame_host());
2056   ASSERT_TRUE(ExecuteScript(
2057       shell(), JsReplace("w.location.href = $1;", popup_url_three)));
2058   commit_observer.WaitForCommit();
2059 
2060   CheckURLOriginAndNetworkIsolationKey(popup_frame, popup_url_three,
2061                                        popup_origin_three,
2062                                        expected_popup_key_three);
2063   if (AreAllSitesIsolatedForTesting()) {
2064     EXPECT_NE(root->current_frame_host()->GetProcess(),
2065               popup_frame->current_frame_host()->GetProcess());
2066   }
2067 }
2068 
2069 // Navigating an iframe to about:blank sets the NetworkIsolationKey differently
2070 // than creating a new frame at about:blank, so needs to be tested.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,NetworkIsolationKeyNavigateIframeToAboutBlank)2071 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
2072                        NetworkIsolationKeyNavigateIframeToAboutBlank) {
2073   GURL main_frame_url(embedded_test_server()->GetURL("/page_with_iframe.html"));
2074   url::Origin origin = url::Origin::Create(main_frame_url);
2075   net::NetworkIsolationKey expected_network_isolation_key =
2076       net::NetworkIsolationKey(origin, origin);
2077 
2078   ASSERT_TRUE(NavigateToURL(shell(), main_frame_url));
2079   FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
2080                             ->GetFrameTree()
2081                             ->root();
2082   CheckURLOriginAndNetworkIsolationKey(root, main_frame_url, origin,
2083                                        expected_network_isolation_key);
2084   ASSERT_EQ(1u, root->child_count());
2085 
2086   CheckURLOriginAndNetworkIsolationKey(
2087       root->child_at(0), embedded_test_server()->GetURL("/title1.html"), origin,
2088       expected_network_isolation_key);
2089   RenderFrameHost* iframe = root->child_at(0)->current_frame_host();
2090 
2091   TestFrameNavigationObserver commit_observer(iframe);
2092   ASSERT_TRUE(ExecuteScript(iframe, "window.location = 'about:blank'"));
2093   commit_observer.WaitForCommit();
2094 
2095   ASSERT_EQ(1u, root->child_count());
2096   CheckURLOriginAndNetworkIsolationKey(root->child_at(0), GURL("about:blank"),
2097                                        origin, expected_network_isolation_key);
2098   // Site-for-cookies should be consistent with the NetworkIsolationKey.
2099   EXPECT_TRUE(
2100       root->child_at(0)
2101           ->current_frame_host()
2102           ->ComputeSiteForCookies()
2103           .IsFirstParty(
2104               expected_network_isolation_key.GetTopFrameOrigin()->GetURL()));
2105 }
2106 
2107 // An iframe that starts at about:blank and is itself nested in a cross-site
2108 // iframe should have the same NetworkIsolationKey as its parent.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,NetworkIsolationKeyNestedCrossSiteAboutBlankIframe)2109 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
2110                        NetworkIsolationKeyNestedCrossSiteAboutBlankIframe) {
2111   const char kSiteA[] = "a.test";
2112   const char kSiteB[] = "b.test";
2113 
2114   // Navigation and creation paths for determining about:blank's
2115   // NetworkIsolationKey are different. This test is for the NIK-on-creation
2116   // path, so need a URL that will start with a nested about:blank iframe.
2117   GURL nested_iframe_url = GURL("about:blank");
2118   GURL cross_site_iframe_url(embedded_test_server()->GetURL(
2119       kSiteB, net::test_server::GetFilePathWithReplacements(
2120                   "/page_with_iframe.html",
2121                   base::StringPairs{
2122                       {"title1.html", nested_iframe_url.spec().c_str()}})));
2123   GURL main_frame_url(embedded_test_server()->GetURL(
2124       kSiteA, net::test_server::GetFilePathWithReplacements(
2125                   "/page_with_iframe.html",
2126                   base::StringPairs{
2127                       {"title1.html", cross_site_iframe_url.spec().c_str()}})));
2128 
2129   // This should be the origin for both the iframes.
2130   url::Origin iframe_origin = url::Origin::Create(cross_site_iframe_url);
2131 
2132   url::Origin main_frame_origin = url::Origin::Create(main_frame_url);
2133 
2134   net::NetworkIsolationKey expected_iframe_network_isolation_key(
2135       main_frame_origin, iframe_origin);
2136   net::NetworkIsolationKey expected_main_frame_network_isolation_key(
2137       main_frame_origin, main_frame_origin);
2138 
2139   ASSERT_TRUE(NavigateToURL(shell(), main_frame_url));
2140   FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
2141                             ->GetFrameTree()
2142                             ->root();
2143   CheckURLOriginAndNetworkIsolationKey(
2144       root, main_frame_url, main_frame_origin,
2145       expected_main_frame_network_isolation_key);
2146 
2147   ASSERT_EQ(1u, root->child_count());
2148   FrameTreeNode* cross_site_iframe = root->child_at(0);
2149   CheckURLOriginAndNetworkIsolationKey(cross_site_iframe, cross_site_iframe_url,
2150                                        iframe_origin,
2151                                        expected_iframe_network_isolation_key);
2152   // Cross site iframes should have an empty site-for-cookies.
2153   EXPECT_TRUE(cross_site_iframe->current_frame_host()
2154                   ->ComputeSiteForCookies()
2155                   .IsNull());
2156 
2157   ASSERT_EQ(1u, cross_site_iframe->child_count());
2158   FrameTreeNode* nested_iframe = cross_site_iframe->child_at(0);
2159   CheckURLOriginAndNetworkIsolationKey(nested_iframe, nested_iframe_url,
2160                                        iframe_origin,
2161                                        expected_iframe_network_isolation_key);
2162   // Cross site iframes should have an empty site-for-cookies.
2163   EXPECT_TRUE(
2164       nested_iframe->current_frame_host()->ComputeSiteForCookies().IsNull());
2165 }
2166 
2167 // An iframe that's navigated to about:blank and is itself nested in a
2168 // cross-site iframe should have the same NetworkIsolationKey as its parent. The
2169 // navigation path is a bit different from the creation path in the above path,
2170 // so needs to be tested as well.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,NetworkIsolationKeyNavigateNestedCrossSiteAboutBlankIframe)2171 IN_PROC_BROWSER_TEST_F(
2172     RenderFrameHostImplBrowserTest,
2173     NetworkIsolationKeyNavigateNestedCrossSiteAboutBlankIframe) {
2174   const char kSiteA[] = "a.test";
2175   const char kSiteB[] = "b.test";
2176   const char kSiteC[] = "c.test";
2177 
2178   // Start with a.test iframing b.test iframing c.test.  Innermost iframe should
2179   // not be on the same site as the middle iframe, so that navigations to/from
2180   // about:blank initiated by b.test change its origin.
2181   GURL innermost_iframe_url(
2182       embedded_test_server()->GetURL(kSiteC, "/title1.html"));
2183   GURL middle_iframe_url(embedded_test_server()->GetURL(
2184       kSiteB, net::test_server::GetFilePathWithReplacements(
2185                   "/page_with_iframe.html",
2186                   base::StringPairs{
2187                       {"title1.html", innermost_iframe_url.spec().c_str()}})));
2188   GURL main_frame_url(embedded_test_server()->GetURL(
2189       kSiteA, net::test_server::GetFilePathWithReplacements(
2190                   "/page_with_iframe.html",
2191                   base::StringPairs{
2192                       {"title1.html", middle_iframe_url.spec().c_str()}})));
2193 
2194   url::Origin innermost_iframe_origin =
2195       url::Origin::Create(innermost_iframe_url);
2196   url::Origin middle_iframe_origin = url::Origin::Create(middle_iframe_url);
2197   url::Origin main_frame_origin = url::Origin::Create(main_frame_url);
2198 
2199   net::NetworkIsolationKey expected_innermost_iframe_network_isolation_key(
2200       main_frame_origin, innermost_iframe_origin);
2201   net::NetworkIsolationKey expected_middle_iframe_network_isolation_key(
2202       main_frame_origin, middle_iframe_origin);
2203   net::NetworkIsolationKey expected_main_frame_network_isolation_key(
2204       main_frame_origin, main_frame_origin);
2205 
2206   ASSERT_TRUE(NavigateToURL(shell(), main_frame_url));
2207   FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
2208                             ->GetFrameTree()
2209                             ->root();
2210   CheckURLOriginAndNetworkIsolationKey(
2211       root, main_frame_url, main_frame_origin,
2212       expected_main_frame_network_isolation_key);
2213 
2214   ASSERT_EQ(1u, root->child_count());
2215   FrameTreeNode* middle_iframe = root->child_at(0);
2216   CheckURLOriginAndNetworkIsolationKey(
2217       middle_iframe, middle_iframe_url, middle_iframe_origin,
2218       expected_middle_iframe_network_isolation_key);
2219   // Cross site iframes should have an empty site-for-cookies.
2220   EXPECT_TRUE(
2221       middle_iframe->current_frame_host()->ComputeSiteForCookies().IsNull());
2222 
2223   ASSERT_EQ(1u, middle_iframe->child_count());
2224   FrameTreeNode* innermost_iframe = middle_iframe->child_at(0);
2225   CheckURLOriginAndNetworkIsolationKey(
2226       innermost_iframe, innermost_iframe_url, innermost_iframe_origin,
2227       expected_innermost_iframe_network_isolation_key);
2228   // Cross site iframes should have an empty site-for-cookies.
2229   EXPECT_TRUE(
2230       innermost_iframe->current_frame_host()->ComputeSiteForCookies().IsNull());
2231 
2232   // The middle iframe navigates the innermost iframe to about:blank. It should
2233   // then have the same NetworkIsolationKey as the middle iframe.
2234   TestNavigationObserver nav_observer1(shell()->web_contents());
2235   ASSERT_TRUE(ExecJs(
2236       middle_iframe->current_frame_host(),
2237       "var iframe = "
2238       "document.getElementById('test_iframe');iframe.src='about:blank';"));
2239   nav_observer1.WaitForNavigationFinished();
2240   CheckURLOriginAndNetworkIsolationKey(
2241       innermost_iframe, GURL("about:blank"), middle_iframe_origin,
2242       expected_middle_iframe_network_isolation_key);
2243   // Cross site iframes should have an empty site-for-cookies.
2244   EXPECT_TRUE(
2245       middle_iframe->current_frame_host()->ComputeSiteForCookies().IsNull());
2246 
2247   // The innermost iframe, now at about:blank, navigates itself back its
2248   // original location, which should make it use c.test's NIK again.
2249   TestNavigationObserver nav_observer2(shell()->web_contents());
2250   ASSERT_TRUE(
2251       ExecJs(innermost_iframe->current_frame_host(), "window.history.back();"));
2252   nav_observer2.WaitForNavigationFinished();
2253   CheckURLOriginAndNetworkIsolationKey(
2254       innermost_iframe, innermost_iframe_url, innermost_iframe_origin,
2255       expected_innermost_iframe_network_isolation_key);
2256   // Cross site iframes should have an empty site-for-cookies.
2257   EXPECT_TRUE(
2258       innermost_iframe->current_frame_host()->ComputeSiteForCookies().IsNull());
2259 
2260   // The innermost iframe, now at c.test, navigates itself back to about:blank.
2261   // Despite c.test initiating the navigation, the iframe should be using
2262   // b.test's NIK, since the navigation entry was created by a navigation
2263   // initiated by b.test.
2264   TestNavigationObserver nav_observer3(shell()->web_contents());
2265   ASSERT_TRUE(ExecJs(innermost_iframe->current_frame_host(),
2266                      "window.history.forward();"));
2267   nav_observer3.WaitForNavigationFinished();
2268   CheckURLOriginAndNetworkIsolationKey(
2269       innermost_iframe, GURL("about:blank"), middle_iframe_origin,
2270       expected_middle_iframe_network_isolation_key);
2271   // Cross site iframes should have an empty site-for-cookies.
2272   EXPECT_TRUE(
2273       innermost_iframe->current_frame_host()->ComputeSiteForCookies().IsNull());
2274 }
2275 
2276 // Verify that if the UMA histograms are correctly recording if interface
2277 // broker requests are getting dropped because they racily arrive from the
2278 // previously active document (after the next navigation already committed).
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,DroppedInterfaceRequestCounter)2279 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
2280                        DroppedInterfaceRequestCounter) {
2281   const GURL kUrl1(embedded_test_server()->GetURL("/title1.html"));
2282   const GURL kUrl2(embedded_test_server()->GetURL("/title2.html"));
2283   const GURL kUrl3(embedded_test_server()->GetURL("/title3.html"));
2284   const GURL kUrl4(embedded_test_server()->GetURL("/empty.html"));
2285 
2286   // The 31-bit hash of the string "content.mojom.MojoWebTestHelper".
2287   const int32_t kHashOfContentMojomMojoWebTestHelper = 0x77b7b3d6;
2288 
2289   // Client ends of the fake interface broker receivers injected for the first
2290   // and second navigations.
2291   mojo::Remote<blink::mojom::BrowserInterfaceBroker> interface_broker_1;
2292   mojo::Remote<blink::mojom::BrowserInterfaceBroker> interface_broker_2;
2293 
2294   base::RunLoop wait_until_connection_error_loop_1;
2295   base::RunLoop wait_until_connection_error_loop_2;
2296 
2297   {
2298     ScopedFakeInterfaceBrokerRequestInjector injector(shell()->web_contents());
2299     injector.set_fake_receiver_for_next_commit(
2300         interface_broker_1.BindNewPipeAndPassReceiver());
2301     interface_broker_1.set_disconnect_handler(
2302         wait_until_connection_error_loop_1.QuitClosure());
2303     ASSERT_TRUE(NavigateToURL(shell(), kUrl1));
2304   }
2305 
2306   {
2307     ScopedFakeInterfaceBrokerRequestInjector injector(shell()->web_contents());
2308     injector.set_fake_receiver_for_next_commit(
2309         interface_broker_2.BindNewPipeAndPassReceiver());
2310     interface_broker_2.set_disconnect_handler(
2311         wait_until_connection_error_loop_2.QuitClosure());
2312     ASSERT_TRUE(NavigateToURL(shell(), kUrl2));
2313   }
2314 
2315   // Simulate two interface requests corresponding to the first navigation
2316   // arrived after the second navigation was committed, hence were dropped.
2317   interface_broker_1->GetInterface(
2318       mojo::PendingReceiver<mojom::MojoWebTestHelper>(
2319           CreateDisconnectedMessagePipeHandle()));
2320   interface_broker_1->GetInterface(
2321       mojo::PendingReceiver<mojom::MojoWebTestHelper>(
2322           CreateDisconnectedMessagePipeHandle()));
2323 
2324   // RFHI destroys the DroppedInterfaceRequestLogger from navigation `n` on
2325   // navigation `n+2`. Histrograms are recorded on destruction, there should
2326   // be a single sample indicating two requests having been dropped for the
2327   // first URL.
2328   {
2329     base::HistogramTester histogram_tester;
2330     ASSERT_TRUE(NavigateToURL(shell(), kUrl3));
2331     histogram_tester.ExpectUniqueSample(
2332         "RenderFrameHostImpl.DroppedInterfaceRequests", 2, 1);
2333     histogram_tester.ExpectUniqueSample(
2334         "RenderFrameHostImpl.DroppedInterfaceRequestName",
2335         kHashOfContentMojomMojoWebTestHelper, 2);
2336   }
2337 
2338   // Simulate one interface request dropped for the second URL.
2339   interface_broker_2->GetInterface(
2340       mojo::PendingReceiver<mojom::MojoWebTestHelper>(
2341           CreateDisconnectedMessagePipeHandle()));
2342 
2343   // A final navigation should record the sample from the second URL.
2344   {
2345     base::HistogramTester histogram_tester;
2346     ASSERT_TRUE(NavigateToURL(shell(), kUrl4));
2347     histogram_tester.ExpectUniqueSample(
2348         "RenderFrameHostImpl.DroppedInterfaceRequests", 1, 1);
2349     histogram_tester.ExpectUniqueSample(
2350         "RenderFrameHostImpl.DroppedInterfaceRequestName",
2351         kHashOfContentMojomMojoWebTestHelper, 1);
2352   }
2353 
2354   // Both the DroppedInterfaceRequestLogger for the first and second URLs are
2355   // destroyed -- even more interfacerequests should not cause any crashes.
2356   interface_broker_1->GetInterface(
2357       mojo::PendingReceiver<mojom::MojoWebTestHelper>(
2358           CreateDisconnectedMessagePipeHandle()));
2359   interface_broker_2->GetInterface(
2360       mojo::PendingReceiver<mojom::MojoWebTestHelper>(
2361           CreateDisconnectedMessagePipeHandle()));
2362 
2363   // The interface connections should be broken.
2364   wait_until_connection_error_loop_1.Run();
2365   wait_until_connection_error_loop_2.Run();
2366 }
2367 
2368 // Regression test for https://crbug.com/852350
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,GetCanonicalUrlAfterRendererCrash)2369 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
2370                        GetCanonicalUrlAfterRendererCrash) {
2371   EXPECT_TRUE(NavigateToURL(
2372       shell(), GetTestUrl("render_frame_host", "beforeunload.html")));
2373   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
2374 
2375   WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
2376   RenderFrameHostImpl* main_frame =
2377       static_cast<RenderFrameHostImpl*>(wc->GetMainFrame());
2378 
2379   // Make the renderer crash.
2380   RenderProcessHost* renderer_process = main_frame->GetProcess();
2381   RenderProcessHostWatcher crash_observer(
2382       renderer_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
2383   renderer_process->Shutdown(0);
2384   crash_observer.Wait();
2385 
2386   main_frame->GetCanonicalUrlForSharing(base::DoNothing());
2387 }
2388 
2389 // This test makes sure that when a blocked frame commits with a different URL,
2390 // it doesn't lead to a leaked NavigationHandle. This is a regression test for
2391 // https://crbug.com/872803.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,ErrorPagesShouldntLeakNavigationHandles)2392 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
2393                        ErrorPagesShouldntLeakNavigationHandles) {
2394   GURL main_url(embedded_test_server()->GetURL(
2395       "foo.com", "/frame_tree/page_with_one_frame.html"));
2396   EXPECT_TRUE(NavigateToURL(shell(), main_url));
2397 
2398   GURL blocked_url(embedded_test_server()->GetURL(
2399       "blocked.com", "/frame-ancestors-none.html"));
2400   WebContents* web_contents = shell()->web_contents();
2401   NavigationHandleObserver navigation_observer(web_contents, blocked_url);
2402   EXPECT_TRUE(NavigateIframeToURL(web_contents, "child0", blocked_url));
2403 
2404   // Verify that the NavigationHandle / NavigationRequest didn't leak.
2405   RenderFrameHostImpl* frame = static_cast<RenderFrameHostImpl*>(
2406       shell()->web_contents()->GetAllFrames()[1]);
2407   EXPECT_FALSE(frame->HasPendingCommitNavigation());
2408 
2409   // TODO(lukasza, clamy): https://crbug.com/784904: Verify that
2410   // WebContentsObserver::DidFinishNavigation was called with the same
2411   // NavigationHandle as WebContentsObserver::DidStartNavigation. This requires
2412   // properly matching the commit IPC to the NavigationHandle (ignoring that
2413   // their URLs do not match - matching instead using navigation id or mojo
2414   // interface identity).
2415 
2416   // TODO(https://crbug.com/759184): Verify CSP frame-ancestors in the browser
2417   // process. Currently, this is done by the renderer process, which commits an
2418   // empty document with success instead.
2419   EXPECT_TRUE(navigation_observer.has_committed());
2420   if (base::FeatureList::IsEnabled(
2421           network::features::kOutOfBlinkFrameAncestors)) {
2422     EXPECT_TRUE(navigation_observer.is_error());
2423     EXPECT_EQ(blocked_url, frame->GetLastCommittedURL());
2424     EXPECT_EQ(net::ERR_BLOCKED_BY_RESPONSE,
2425               navigation_observer.net_error_code());
2426   } else {
2427     EXPECT_FALSE(navigation_observer.is_error());
2428     EXPECT_EQ(GURL("data:,"), navigation_observer.last_committed_url());
2429     EXPECT_EQ(net::Error::OK, navigation_observer.net_error_code());
2430   }
2431 }
2432 
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,BeforeUnloadDialogSuppressedForDiscard)2433 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
2434                        BeforeUnloadDialogSuppressedForDiscard) {
2435   WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
2436   TestJavaScriptDialogManager dialog_manager;
2437   wc->SetDelegate(&dialog_manager);
2438 
2439   EXPECT_TRUE(NavigateToURL(
2440       shell(), GetTestUrl("render_frame_host", "beforeunload.html")));
2441   // Disable the hang monitor, otherwise there will be a race between the
2442   // beforeunload dialog and the beforeunload hang timer.
2443   wc->GetMainFrame()->DisableBeforeUnloadHangMonitorForTesting();
2444 
2445   // Give the page a user gesture so javascript beforeunload works, and then
2446   // dispatch a before unload with discard as a reason. This should return
2447   // without any dialog being seen.
2448   wc->GetMainFrame()->ExecuteJavaScriptWithUserGestureForTests(
2449       base::string16());
2450   wc->GetMainFrame()->DispatchBeforeUnload(
2451       RenderFrameHostImpl::BeforeUnloadType::DISCARD, false);
2452   dialog_manager.Wait();
2453   EXPECT_EQ(0, dialog_manager.num_beforeunload_dialogs_seen());
2454   EXPECT_EQ(1, dialog_manager.num_beforeunload_fired_seen());
2455   EXPECT_FALSE(dialog_manager.proceed());
2456 
2457   wc->SetDelegate(nullptr);
2458   wc->SetJavaScriptDialogManagerForTesting(nullptr);
2459 }
2460 
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,PendingDialogMakesDiscardUnloadReturnFalse)2461 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
2462                        PendingDialogMakesDiscardUnloadReturnFalse) {
2463   WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
2464   TestJavaScriptDialogManager dialog_manager;
2465   wc->SetDelegate(&dialog_manager);
2466 
2467   EXPECT_TRUE(NavigateToURL(
2468       shell(), GetTestUrl("render_frame_host", "beforeunload.html")));
2469   // Disable the hang monitor, otherwise there will be a race between the
2470   // beforeunload dialog and the beforeunload hang timer.
2471   wc->GetMainFrame()->DisableBeforeUnloadHangMonitorForTesting();
2472 
2473   // Give the page a user gesture so javascript beforeunload works, and then
2474   // dispatch a before unload with discard as a reason. This should return
2475   // without any dialog being seen.
2476   wc->GetMainFrame()->ExecuteJavaScriptWithUserGestureForTests(
2477       base::string16());
2478 
2479   // Launch an alert javascript dialog. This pending dialog should block a
2480   // subsequent discarding before unload request.
2481   wc->GetMainFrame()->ExecuteJavaScriptForTests(
2482       base::ASCIIToUTF16("setTimeout(function(){alert('hello');}, 10);"),
2483       base::NullCallback());
2484   dialog_manager.Wait();
2485   EXPECT_EQ(0, dialog_manager.num_beforeunload_dialogs_seen());
2486   EXPECT_EQ(0, dialog_manager.num_beforeunload_fired_seen());
2487 
2488   // Dispatch a before unload request while the first is still blocked
2489   // on the dialog, and expect it to return false immediately (synchronously).
2490   wc->GetMainFrame()->DispatchBeforeUnload(
2491       RenderFrameHostImpl::BeforeUnloadType::DISCARD, false);
2492   dialog_manager.Wait();
2493   EXPECT_EQ(0, dialog_manager.num_beforeunload_dialogs_seen());
2494   EXPECT_EQ(1, dialog_manager.num_beforeunload_fired_seen());
2495   EXPECT_FALSE(dialog_manager.proceed());
2496 
2497   // Clear the existing javascript dialog so that the associated IPC message
2498   // doesn't leak.
2499   dialog_manager.Run(true, base::string16());
2500 
2501   wc->SetDelegate(nullptr);
2502   wc->SetJavaScriptDialogManagerForTesting(nullptr);
2503 }
2504 
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,NotifiesProcessHostOfAudibleAudio)2505 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
2506                        NotifiesProcessHostOfAudibleAudio) {
2507   const auto RunPostedTasks = []() {
2508     base::RunLoop run_loop;
2509     base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
2510                                                   run_loop.QuitClosure());
2511     run_loop.Run();
2512   };
2513 
2514   // Note: Just using the beforeunload.html test document to spin-up a
2515   // renderer. Any document will do.
2516   EXPECT_TRUE(NavigateToURL(
2517       shell(), GetTestUrl("render_frame_host", "beforeunload.html")));
2518   EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
2519 
2520   auto* frame = static_cast<RenderFrameHostImpl*>(
2521       shell()->web_contents()->GetMainFrame());
2522   auto* process = static_cast<RenderProcessHostImpl*>(frame->GetProcess());
2523   ASSERT_EQ(0, process->get_media_stream_count_for_testing());
2524 
2525   // Audible audio output should cause the media stream count to increment.
2526   frame->OnAudibleStateChanged(true);
2527   RunPostedTasks();
2528   EXPECT_EQ(1, process->get_media_stream_count_for_testing());
2529 
2530   // Silence should cause the media stream count to decrement.
2531   frame->OnAudibleStateChanged(false);
2532   RunPostedTasks();
2533   EXPECT_EQ(0, process->get_media_stream_count_for_testing());
2534 
2535   // Start audible audio output again, and then crash the renderer. Expect the
2536   // media stream count to be zero after the crash.
2537   frame->OnAudibleStateChanged(true);
2538   RunPostedTasks();
2539   EXPECT_EQ(1, process->get_media_stream_count_for_testing());
2540   RenderProcessHostWatcher crash_observer(
2541       process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
2542   process->Shutdown(0);
2543   crash_observer.Wait();
2544   RunPostedTasks();
2545   EXPECT_EQ(0, process->get_media_stream_count_for_testing());
2546 }
2547 
2548 #if defined(OS_CHROMEOS) || defined(OS_LINUX)
2549 // ChromeOS and Linux failures are tracked in https://crbug.com/954217
2550 #define MAYBE_VisibilityScrolledOutOfView DISABLED_VisibilityScrolledOutOfView
2551 #else
2552 #define MAYBE_VisibilityScrolledOutOfView VisibilityScrolledOutOfView
2553 #endif
2554 // Test that a frame is visible/hidden depending on its WebContents visibility
2555 // state.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,MAYBE_VisibilityScrolledOutOfView)2556 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
2557                        MAYBE_VisibilityScrolledOutOfView) {
2558   WebContentsImpl* web_contents =
2559       static_cast<WebContentsImpl*>(shell()->web_contents());
2560 
2561   GURL main_frame(embedded_test_server()->GetURL("/iframe_out_of_view.html"));
2562   GURL child_url(embedded_test_server()->GetURL("/hello.html"));
2563 
2564   // This will set up the page frame tree as A(A1()).
2565   ASSERT_TRUE(NavigateToURL(shell(), main_frame));
2566   FrameTreeNode* root = web_contents->GetFrameTree()->root();
2567   FrameTreeNode* nested_iframe_node = root->child_at(0);
2568   NavigateFrameToURL(nested_iframe_node, child_url);
2569 
2570   ASSERT_EQ(blink::mojom::FrameVisibility::kRenderedOutOfViewport,
2571             nested_iframe_node->current_frame_host()->visibility());
2572 }
2573 
2574 // Test that a frame is visible/hidden depending on its WebContents visibility
2575 // state.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,VisibilityChildInView)2576 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest, VisibilityChildInView) {
2577   WebContentsImpl* web_contents =
2578       static_cast<WebContentsImpl*>(shell()->web_contents());
2579 
2580   GURL main_frame(embedded_test_server()->GetURL("/iframe_clipped.html"));
2581   GURL child_url(embedded_test_server()->GetURL("/hello.html"));
2582 
2583   // This will set up the page frame tree as A(A1()).
2584   ASSERT_TRUE(NavigateToURL(shell(), main_frame));
2585   FrameTreeNode* root = web_contents->GetFrameTree()->root();
2586   FrameTreeNode* nested_iframe_node = root->child_at(0);
2587   NavigateFrameToURL(nested_iframe_node, child_url);
2588 
2589   ASSERT_EQ(blink::mojom::FrameVisibility::kRenderedInViewport,
2590             nested_iframe_node->current_frame_host()->visibility());
2591 }
2592 
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,OriginOfFreshFrame_Subframe_NavCancelledByDocWrite)2593 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
2594                        OriginOfFreshFrame_Subframe_NavCancelledByDocWrite) {
2595   WebContents* web_contents = shell()->web_contents();
2596   NavigationController& controller = web_contents->GetController();
2597   GURL main_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
2598   ASSERT_TRUE(NavigateToURL(shell(), main_url));
2599   EXPECT_EQ(1, controller.GetEntryCount());
2600   url::Origin main_origin = url::Origin::Create(main_url);
2601 
2602   // document.open should cancel the cross-origin navigation to '/hung' and the
2603   // subframe should remain on the parent/initiator origin.
2604   const char kScriptTemplate[] = R"(
2605       const frame = document.createElement('iframe');
2606       frame.src = $1;
2607       document.body.appendChild(frame);
2608 
2609       const html = '<!DOCTYPE html><html><body>Hello world!</body></html>';
2610       const doc = frame.contentDocument;
2611       doc.open();
2612       doc.write(html);
2613       doc.close();
2614 
2615       frame.contentWindow.origin;
2616   )";
2617   GURL cross_site_url(embedded_test_server()->GetURL("bar.com", "/hung"));
2618   std::string script = JsReplace(kScriptTemplate, cross_site_url);
2619   EXPECT_EQ(main_origin.Serialize(), EvalJs(web_contents, script));
2620 
2621   // The subframe navigation should be cancelled and therefore shouldn't
2622   // contribute an extra history entry.
2623   EXPECT_EQ(1, controller.GetEntryCount());
2624 
2625   // Browser-side origin should match the renderer-side origin.
2626   // See also https://crbug.com/932067.
2627   ASSERT_EQ(2u, web_contents->GetAllFrames().size());
2628   RenderFrameHost* subframe = web_contents->GetAllFrames()[1];
2629   EXPECT_EQ(main_origin, subframe->GetLastCommittedOrigin());
2630 }
2631 
2632 class RenderFrameHostCreatedObserver : public WebContentsObserver {
2633  public:
RenderFrameHostCreatedObserver(WebContents * web_contents)2634   explicit RenderFrameHostCreatedObserver(WebContents* web_contents)
2635       : WebContentsObserver(web_contents) {}
2636 
Wait()2637   RenderFrameHost* Wait() {
2638     if (!new_frame_)
2639       run_loop_.Run();
2640 
2641     return new_frame_;
2642   }
2643 
2644  private:
RenderFrameCreated(RenderFrameHost * render_frame_host)2645   void RenderFrameCreated(RenderFrameHost* render_frame_host) override {
2646     new_frame_ = render_frame_host;
2647     run_loop_.Quit();
2648   }
2649 
2650   base::RunLoop run_loop_;
2651   RenderFrameHost* new_frame_ = nullptr;
2652 
2653   DISALLOW_COPY_AND_ASSIGN(RenderFrameHostCreatedObserver);
2654 };
2655 
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,OriginOfFreshFrame_SandboxedSubframe)2656 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
2657                        OriginOfFreshFrame_SandboxedSubframe) {
2658   WebContents* web_contents = shell()->web_contents();
2659   NavigationController& controller = web_contents->GetController();
2660   GURL main_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
2661   ASSERT_TRUE(NavigateToURL(shell(), main_url));
2662   EXPECT_EQ(1, controller.GetEntryCount());
2663   url::Origin main_origin = url::Origin::Create(main_url);
2664 
2665   // Navigate a sandboxed frame to a cross-origin '/hung'.
2666   RenderFrameHostCreatedObserver subframe_observer(web_contents);
2667   const char kScriptTemplate[] = R"(
2668       const frame = document.createElement('iframe');
2669       frame.sandbox = 'allow-scripts';
2670       frame.src = $1;
2671       document.body.appendChild(frame);
2672   )";
2673   GURL cross_site_url(embedded_test_server()->GetURL("bar.com", "/hung"));
2674   std::string script = JsReplace(kScriptTemplate, cross_site_url);
2675   EXPECT_TRUE(ExecJs(web_contents, script));
2676 
2677   // Wait for a new subframe, but ignore the frame returned by
2678   // |subframe_observer| (it might be the speculative one, not the current one).
2679   subframe_observer.Wait();
2680   ASSERT_EQ(2u, web_contents->GetAllFrames().size());
2681   RenderFrameHost* subframe = web_contents->GetAllFrames()[1];
2682 
2683   // The browser-side origin of the *sandboxed* subframe should be set to an
2684   // *opaque* origin (with the parent's origin as the precursor origin).
2685   EXPECT_TRUE(subframe->GetLastCommittedOrigin().opaque());
2686   EXPECT_EQ(
2687       main_origin.GetTupleOrPrecursorTupleIfOpaque(),
2688       subframe->GetLastCommittedOrigin().GetTupleOrPrecursorTupleIfOpaque());
2689 
2690   // Note that the test cannot check the renderer-side origin of the frame:
2691   // - Scripts cannot be executed before the frame commits,
2692   // - The parent cannot document.write into the *sandboxed* frame.
2693 }
2694 
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,OriginOfFreshFrame_Subframe_AboutBlankAndThenDocWrite)2695 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
2696                        OriginOfFreshFrame_Subframe_AboutBlankAndThenDocWrite) {
2697   WebContents* web_contents = shell()->web_contents();
2698   NavigationController& controller = web_contents->GetController();
2699   GURL main_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
2700   ASSERT_TRUE(NavigateToURL(shell(), main_url));
2701   EXPECT_EQ(1, controller.GetEntryCount());
2702   url::Origin main_origin = url::Origin::Create(main_url);
2703 
2704   // Create a new about:blank subframe and document.write into it.
2705   TestNavigationObserver load_observer(web_contents);
2706   RenderFrameHostCreatedObserver subframe_observer(web_contents);
2707   const char kScript[] = R"(
2708       const frame = document.createElement('iframe');
2709       // Don't set |frame.src| - have the frame commit an initial about:blank.
2710       document.body.appendChild(frame);
2711 
2712       const html = '<!DOCTYPE html><html><body>Hello world!</body></html>';
2713       const doc = frame.contentDocument;
2714       doc.open();
2715       doc.write(html);
2716       doc.close();
2717   )";
2718   ExecuteScriptAsync(web_contents, kScript);
2719 
2720   // Wait for the new subframe to be created - this will be still before the
2721   // commit of about:blank.
2722   RenderFrameHost* subframe = subframe_observer.Wait();
2723   EXPECT_EQ(main_origin, subframe->GetLastCommittedOrigin());
2724 
2725   // Wait for the about:blank navigation to finish.
2726   load_observer.Wait();
2727 
2728   // The subframe commit to about:blank should not contribute an extra history
2729   // entry.
2730   EXPECT_EQ(1, controller.GetEntryCount());
2731 
2732   // Browser-side origin should match the renderer-side origin.
2733   // See also https://crbug.com/932067.
2734   ASSERT_EQ(2u, web_contents->GetAllFrames().size());
2735   RenderFrameHost* subframe2 = web_contents->GetAllFrames()[1];
2736   EXPECT_EQ(subframe, subframe2);  // No swaps are expected.
2737   EXPECT_EQ(main_origin, subframe2->GetLastCommittedOrigin());
2738   EXPECT_EQ(main_origin.Serialize(), EvalJs(subframe2, "window.origin"));
2739 }
2740 
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,OriginOfFreshFrame_Popup_NavCancelledByDocWrite)2741 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
2742                        OriginOfFreshFrame_Popup_NavCancelledByDocWrite) {
2743   WebContents* web_contents = shell()->web_contents();
2744   GURL main_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
2745   ASSERT_TRUE(NavigateToURL(shell(), main_url));
2746   url::Origin main_origin = url::Origin::Create(main_url);
2747 
2748   // document.open should cancel the cross-origin navigation to '/hung' and the
2749   // popup should remain on the initiator origin.
2750   WebContentsAddedObserver popup_observer;
2751   const char kScriptTemplate[] = R"(
2752       var popup = window.open($1, 'popup');
2753 
2754       const html = '<!DOCTYPE html><html><body>Hello world!</body></html>';
2755       const doc = popup.document;
2756       doc.open();
2757       doc.write(html);
2758       doc.close();
2759 
2760       popup.origin;
2761   )";
2762   GURL cross_site_url(embedded_test_server()->GetURL("bar.com", "/hung"));
2763   std::string script = JsReplace(kScriptTemplate, cross_site_url);
2764   EXPECT_EQ(main_origin.Serialize(), EvalJs(web_contents, script));
2765 
2766   // Browser-side origin should match the renderer-side origin.
2767   // See also https://crbug.com/932067.
2768   WebContents* popup = popup_observer.GetWebContents();
2769   EXPECT_EQ(main_origin, popup->GetMainFrame()->GetLastCommittedOrigin());
2770 
2771   // The popup navigation should be cancelled and therefore shouldn't
2772   // contribute an extra history entry.
2773   EXPECT_EQ(0, popup->GetController().GetEntryCount());
2774 }
2775 
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,OriginOfFreshFrame_Popup_AboutBlankAndThenDocWrite)2776 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
2777                        OriginOfFreshFrame_Popup_AboutBlankAndThenDocWrite) {
2778   WebContents* web_contents = shell()->web_contents();
2779   GURL main_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
2780   ASSERT_TRUE(NavigateToURL(shell(), main_url));
2781   url::Origin main_origin = url::Origin::Create(main_url);
2782 
2783   // Create a new about:blank popup and document.write into it.
2784   WebContentsAddedObserver popup_observer;
2785   TestNavigationObserver load_observer(web_contents);
2786   const char kScript[] = R"(
2787       // Empty |url| argument means that the popup will commit an initial
2788       // about:blank.
2789       var popup = window.open('', 'popup');
2790 
2791       const html = '<!DOCTYPE html><html><body>Hello world!</body></html>';
2792       const doc = popup.document;
2793       doc.open();
2794       doc.write(html);
2795       doc.close();
2796   )";
2797   ExecuteScriptAsync(web_contents, kScript);
2798 
2799   // Wait for the new popup to be created (this will be before the popup commits
2800   // the initial about:blank page).
2801   WebContents* popup = popup_observer.GetWebContents();
2802   EXPECT_EQ(main_origin, popup->GetMainFrame()->GetLastCommittedOrigin());
2803 
2804   // A round-trip to the renderer process is an indirect way to wait for
2805   // DidCommitProvisionalLoad IPC for the initial about:blank page.
2806   // WaitForLoadStop cannot be used, because this commit won't raise
2807   // NOTIFICATION_LOAD_STOP.
2808   EXPECT_EQ(123, EvalJs(popup, "123"));
2809   EXPECT_EQ(main_origin, popup->GetMainFrame()->GetLastCommittedOrigin());
2810 
2811   // The about:blank navigation shouldn't contribute an extra history entry.
2812   EXPECT_EQ(0, popup->GetController().GetEntryCount());
2813 }
2814 
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,AccessibilityIsRootIframe)2815 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
2816                        AccessibilityIsRootIframe) {
2817   GURL main_url(
2818       embedded_test_server()->GetURL("foo.com", "/page_with_iframe.html"));
2819   ASSERT_TRUE(NavigateToURL(shell(), main_url));
2820 
2821   RenderFrameHostImpl* main_frame = static_cast<RenderFrameHostImpl*>(
2822       shell()->web_contents()->GetMainFrame());
2823   EXPECT_TRUE(main_frame->AccessibilityIsMainFrame());
2824 
2825   ASSERT_EQ(1u, main_frame->child_count());
2826   RenderFrameHostImpl* iframe = main_frame->child_at(0)->current_frame_host();
2827   EXPECT_FALSE(iframe->AccessibilityIsMainFrame());
2828 }
2829 
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,RequestSnapshotAXTreeAfterRenderProcessHostDeath)2830 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
2831                        RequestSnapshotAXTreeAfterRenderProcessHostDeath) {
2832   EXPECT_TRUE(NavigateToURL(shell(), GURL(url::kAboutBlankURL)));
2833   auto* rfh = static_cast<RenderFrameHostImpl*>(
2834       shell()->web_contents()->GetMainFrame());
2835 
2836   // Kill the renderer process.
2837   RenderProcessHostWatcher crash_observer(
2838       rfh->GetProcess(), RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
2839   rfh->GetProcess()->Shutdown(0);
2840   crash_observer.Wait();
2841 
2842   // Call RequestAXSnapshotTree method. The browser process should not crash.
2843   rfh->RequestAXTreeSnapshot(
2844       base::BindOnce([](const ui::AXTreeUpdate& snapshot) { NOTREACHED(); }),
2845       ui::AXMode::kWebContents);
2846 
2847   base::RunLoop().RunUntilIdle();
2848 
2849   // Pass if this didn't crash.
2850 }
2851 
FileChooserCallback(base::RunLoop * run_loop,blink::mojom::FileChooserResultPtr result)2852 void FileChooserCallback(base::RunLoop* run_loop,
2853                          blink::mojom::FileChooserResultPtr result) {
2854   run_loop->Quit();
2855 }
2856 
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,FileChooserAfterRfhDeath)2857 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
2858                        FileChooserAfterRfhDeath) {
2859   EXPECT_TRUE(NavigateToURL(shell(), GURL(url::kAboutBlankURL)));
2860   auto* rfh = static_cast<RenderFrameHostImpl*>(
2861       shell()->web_contents()->GetMainFrame());
2862   mojo::Remote<blink::mojom::FileChooser> chooser =
2863       rfh->BindFileChooserForTesting();
2864 
2865   // Kill the renderer process.
2866   RenderProcessHostWatcher crash_observer(
2867       rfh->GetProcess(), RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
2868   rfh->GetProcess()->Shutdown(0);
2869   crash_observer.Wait();
2870 
2871   // Call FileChooser methods.  The browser process should not crash.
2872   base::RunLoop run_loop1;
2873   chooser->OpenFileChooser(blink::mojom::FileChooserParams::New(),
2874                            base::BindOnce(FileChooserCallback, &run_loop1));
2875   run_loop1.Run();
2876 
2877   base::RunLoop run_loop2;
2878   chooser->EnumerateChosenDirectory(
2879       base::FilePath(), base::BindOnce(FileChooserCallback, &run_loop2));
2880   run_loop2.Run();
2881 
2882   // Pass if this didn't crash.
2883 }
2884 
2885 // Verify that adding an <object> tag which resource is blocked by the network
2886 // stack does not result in terminating the renderer process.
2887 // See https://crbug.com/955777.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,ObjectTagBlockedResource)2888 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
2889                        ObjectTagBlockedResource) {
2890   EXPECT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL(
2891                                          "/page_with_object_fallback.html")));
2892 
2893   GURL object_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
2894   std::unique_ptr<URLLoaderInterceptor> url_interceptor =
2895       URLLoaderInterceptor::SetupRequestFailForURL(object_url,
2896                                                    net::ERR_BLOCKED_BY_CLIENT);
2897 
2898   auto* rfh = static_cast<RenderFrameHostImpl*>(
2899       shell()->web_contents()->GetMainFrame());
2900   TestNavigationObserver observer(shell()->web_contents());
2901   EXPECT_TRUE(ExecJs(shell()->web_contents(),
2902                      JsReplace("setUrl($1, true);", object_url)));
2903   observer.Wait();
2904   EXPECT_EQ(rfh->GetLastCommittedOrigin().Serialize(),
2905             EvalJs(shell()->web_contents(), "window.origin"));
2906 }
2907 
2908 // Regression test for crbug.com/953934. It shouldn't crash if we quickly remove
2909 // an object element in the middle of its failing navigation.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,NoCrashOnRemoveObjectElementWithInvalidData)2910 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
2911                        NoCrashOnRemoveObjectElementWithInvalidData) {
2912   GURL url = GetFileURL(
2913       FILE_PATH_LITERAL("remove_object_element_with_invalid_data.html"));
2914 
2915   RenderProcessHostWatcher crash_observer(
2916       shell()->web_contents(),
2917       RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
2918 
2919   // This navigates to a page with an object element that will fail to load.
2920   // When document load event hits, it'll attempt to remove that object element.
2921   // This might happen while the object element's failed commit is underway.
2922   // To make sure we hit these conditions and that we don't exit the test too
2923   // soon, let's wait until the document.readyState finalizes. We don't really
2924   // care if that succeeds since, in the failing case, the renderer is crashing.
2925   EXPECT_TRUE(NavigateToURL(shell(), url));
2926   ignore_result(
2927       WaitForRenderFrameReady(shell()->web_contents()->GetMainFrame()));
2928 
2929   EXPECT_TRUE(crash_observer.did_exit_normally());
2930 }
2931 
2932 // Test deduplication of SameSite cookie deprecation messages.
2933 // TODO(crbug.com/976475): This test is flaky.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,DISABLED_DeduplicateSameSiteCookieDeprecationMessages)2934 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
2935                        DISABLED_DeduplicateSameSiteCookieDeprecationMessages) {
2936 #if defined(OS_ANDROID)
2937   // TODO(crbug.com/974701): This test is broken on Android that is
2938   // Marshmallow or older.
2939   if (base::android::BuildInfo::GetInstance()->sdk_int() <=
2940       base::android::SDK_VERSION_MARSHMALLOW) {
2941     return;
2942   }
2943 #endif  // defined(OS_ANDROID)
2944 
2945   base::test::ScopedFeatureList feature_list;
2946   feature_list.InitAndEnableFeature(features::kCookieDeprecationMessages);
2947 
2948   WebContentsConsoleObserver console_observer(shell()->web_contents());
2949 
2950   // Test deprecation messages for SameSiteByDefault.
2951   // Set a cookie without SameSite on b.com, then access it in a cross-site
2952   // context.
2953   GURL url =
2954       embedded_test_server()->GetURL("b.com", "/set-cookie?nosamesite=1");
2955   EXPECT_TRUE(NavigateToURL(shell(), url));
2956   ASSERT_EQ(0u, console_observer.messages().size());
2957   url = embedded_test_server()->GetURL(
2958       "a.com", "/cross_site_iframe_factory.html?a(b(),b())");
2959   EXPECT_TRUE(NavigateToURL(shell(), url));
2960   // Only 1 message even though there are 2 cross-site iframes.
2961   EXPECT_EQ(1u, console_observer.messages().size());
2962 
2963   // Test deprecation messages for CookiesWithoutSameSiteMustBeSecure.
2964   // Set a cookie with SameSite=None but without Secure.
2965   url = embedded_test_server()->GetURL(
2966       "c.com", "/set-cookie?samesitenoneinsecure=1;SameSite=None");
2967   EXPECT_TRUE(NavigateToURL(shell(), url));
2968   // The 1 message from before, plus the (different) message for setting the
2969   // SameSite=None insecure cookie.
2970   EXPECT_EQ(2u, console_observer.messages().size());
2971   // Another copy of the message appears because we have navigated.
2972   EXPECT_TRUE(NavigateToURL(shell(), url));
2973   EXPECT_EQ(3u, console_observer.messages().size());
2974   EXPECT_EQ(console_observer.messages()[1].message,
2975             console_observer.messages()[2].message);
2976 }
2977 
2978 // Enable SameSiteByDefaultCookies to test deprecation messages for
2979 // Lax-allow-unsafe.
2980 class RenderFrameHostImplSameSiteByDefaultCookiesBrowserTest
2981     : public RenderFrameHostImplBrowserTest {
2982  public:
SetUp()2983   void SetUp() override {
2984     feature_list_.InitWithFeatures({features::kCookieDeprecationMessages,
2985                                     net::features::kSameSiteByDefaultCookies},
2986                                    {});
2987     RenderFrameHostImplBrowserTest::SetUp();
2988   }
2989 
2990  private:
2991   base::test::ScopedFeatureList feature_list_;
2992 };
2993 
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplSameSiteByDefaultCookiesBrowserTest,DisplaySameSiteCookieDeprecationMessages)2994 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplSameSiteByDefaultCookiesBrowserTest,
2995                        DisplaySameSiteCookieDeprecationMessages) {
2996   WebContentsConsoleObserver console_observer(shell()->web_contents());
2997 
2998   // Test deprecation messages for SameSiteByDefault.
2999   // Set a cookie without SameSite on b.com, then access it in a cross-site
3000   // context.
3001   base::Time set_cookie_time = base::Time::Now();
3002   GURL url =
3003       embedded_test_server()->GetURL("x.com", "/set-cookie?nosamesite=1");
3004   EXPECT_TRUE(NavigateToURL(shell(), url));
3005   // Message does not appear in same-site context (main frame is x).
3006   ASSERT_EQ(0u, console_observer.messages().size());
3007   url = embedded_test_server()->GetURL(
3008       "a.com", "/cross_site_iframe_factory.html?a(x())");
3009   EXPECT_TRUE(NavigateToURL(shell(), url));
3010   // Message appears in cross-site context (a framing x).
3011   EXPECT_EQ(1u, console_observer.messages().size());
3012 
3013   // Test deprecation messages for CookiesWithoutSameSiteMustBeSecure.
3014   // Set a cookie with SameSite=None but without Secure.
3015   url = embedded_test_server()->GetURL(
3016       "c.com", "/set-cookie?samesitenoneinsecure=1;SameSite=None");
3017   EXPECT_TRUE(NavigateToURL(shell(), url));
3018   // The 1 message from before, plus the (different) message for setting the
3019   // SameSite=None insecure cookie.
3020   EXPECT_EQ(2u, console_observer.messages().size());
3021 
3022   // Test deprecation messages for Lax-allow-unsafe.
3023   url = embedded_test_server()->GetURL("a.com",
3024                                        "/form_that_posts_cross_site.html");
3025   EXPECT_TRUE(NavigateToURL(shell(), url));
3026   // Submit the form to make a cross-site POST request to x.com.
3027   TestNavigationObserver form_post_observer(shell()->web_contents(), 1);
3028   EXPECT_TRUE(ExecJs(shell(), "document.getElementById('text-form').submit()"));
3029   form_post_observer.Wait();
3030 
3031   // The test should not take more than 2 minutes.
3032   ASSERT_LT(base::Time::Now() - set_cookie_time, net::kLaxAllowUnsafeMaxAge);
3033   EXPECT_EQ(3u, console_observer.messages().size());
3034 
3035   // Check that the messages were all distinct.
3036   EXPECT_NE(console_observer.messages()[0].message,
3037             console_observer.messages()[1].message);
3038   EXPECT_NE(console_observer.messages()[0].message,
3039             console_observer.messages()[2].message);
3040   EXPECT_NE(console_observer.messages()[1].message,
3041             console_observer.messages()[2].message);
3042 }
3043 
3044 // Test that the SameSite-by-default console warnings are not emitted
3045 // if the cookie would have been rejected for other reasons.
3046 // Regression test for https://crbug.com/1027318.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplSameSiteByDefaultCookiesBrowserTest,NoMessagesIfCookieWouldBeRejectedForOtherReasons)3047 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplSameSiteByDefaultCookiesBrowserTest,
3048                        NoMessagesIfCookieWouldBeRejectedForOtherReasons) {
3049   WebContentsConsoleObserver console_observer(shell()->web_contents());
3050 
3051   GURL url = embedded_test_server()->GetURL(
3052       "x.com", "/set-cookie?cookiewithpath=1;path=/set-cookie");
3053   EXPECT_TRUE(NavigateToURL(shell(), url));
3054   url = embedded_test_server()->GetURL("sub.x.com",
3055                                        "/set-cookie?cookieforsubdomain=1");
3056   EXPECT_TRUE(NavigateToURL(shell(), url));
3057 
3058   ASSERT_EQ(0u, console_observer.messages().size());
3059   url = embedded_test_server()->GetURL(
3060       "a.com", "/cross_site_iframe_factory.html?a(x())");
3061   EXPECT_TRUE(NavigateToURL(shell(), url));
3062   // No messages appear even though x.com is accessed in a cross-site
3063   // context, because the cookies would have been rejected for mismatching path
3064   // or domain anyway.
3065   EXPECT_EQ(0u, console_observer.messages().size());
3066 }
3067 
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,SchedulerTrackedFeatures)3068 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
3069                        SchedulerTrackedFeatures) {
3070   EXPECT_TRUE(
3071       NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
3072   RenderFrameHostImpl* main_frame = reinterpret_cast<RenderFrameHostImpl*>(
3073       shell()->web_contents()->GetMainFrame());
3074   // Simulate getting 0b1 as a feature vector from the renderer.
3075   static_cast<blink::mojom::LocalFrameHost*>(main_frame)
3076       ->DidChangeActiveSchedulerTrackedFeatures(0b1u);
3077   DCHECK_EQ(main_frame->scheduler_tracked_features(), 0b1u);
3078   // Simulate the browser side reporting a feature usage.
3079   main_frame->OnSchedulerTrackedFeatureUsed(
3080       static_cast<blink::scheduler::WebSchedulerTrackedFeature>(1));
3081   DCHECK_EQ(main_frame->scheduler_tracked_features(), 0b11u);
3082   // Simulate a feature vector being updated from the renderer with some
3083   // features being activated and some being deactivated.
3084   static_cast<blink::mojom::LocalFrameHost*>(main_frame)
3085       ->DidChangeActiveSchedulerTrackedFeatures(0b100u);
3086   DCHECK_EQ(main_frame->scheduler_tracked_features(), 0b110u);
3087 
3088   // Navigate away and expect that no values persist the navigation.
3089   // Note that we are still simulating the renderer call, otherwise features
3090   // like "document loaded" will show up here.
3091   EXPECT_TRUE(
3092       NavigateToURL(shell(), embedded_test_server()->GetURL("/title2.html")));
3093   main_frame = reinterpret_cast<RenderFrameHostImpl*>(
3094       shell()->web_contents()->GetMainFrame());
3095   static_cast<blink::mojom::LocalFrameHost*>(main_frame)
3096       ->DidChangeActiveSchedulerTrackedFeatures(0b0u);
3097 }
3098 
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,ComputeSiteForCookiesForNavigation)3099 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
3100                        ComputeSiteForCookiesForNavigation) {
3101   // Start second server for HTTPS.
3102   https_server()->ServeFilesFromSourceDirectory(GetTestDataFilePath());
3103   ASSERT_TRUE(https_server()->Start());
3104 
3105   GURL url = embedded_test_server()->GetURL(
3106       "a.com", "/cross_site_iframe_factory.html?a(a(b(d)),c())");
3107 
3108   FirstPartySchemeContentBrowserClient new_client(url);
3109   ContentBrowserClient* old_client = SetBrowserClientForTesting(&new_client);
3110 
3111   GURL b_url = embedded_test_server()->GetURL("b.com", "/");
3112   GURL c_url = embedded_test_server()->GetURL("c.com", "/");
3113   GURL secure_url = https_server()->GetURL("/");
3114   EXPECT_TRUE(NavigateToURL(shell(), url));
3115 
3116   {
3117     WebContentsImpl* wc =
3118         static_cast<WebContentsImpl*>(shell()->web_contents());
3119     RenderFrameHostImpl* main_frame = wc->GetMainFrame();
3120 
3121     EXPECT_EQ("a.com", main_frame->GetLastCommittedURL().host());
3122     ASSERT_EQ(2u, main_frame->child_count());
3123     FrameTreeNode* child_a = main_frame->child_at(0);
3124     FrameTreeNode* child_c = main_frame->child_at(1);
3125     EXPECT_EQ("a.com", child_a->current_url().host());
3126     EXPECT_EQ("c.com", child_c->current_url().host());
3127 
3128     ASSERT_EQ(1u, child_a->child_count());
3129     FrameTreeNode* child_b = child_a->child_at(0);
3130     EXPECT_EQ("b.com", child_b->current_url().host());
3131     ASSERT_EQ(1u, child_b->child_count());
3132     FrameTreeNode* child_d = child_b->child_at(0);
3133     EXPECT_EQ("d.com", child_d->current_url().host());
3134 
3135     EXPECT_EQ("a.com", main_frame->ComputeSiteForCookiesForNavigation(url)
3136                            .registrable_domain());
3137     EXPECT_EQ("b.com", main_frame->ComputeSiteForCookiesForNavigation(b_url)
3138                            .registrable_domain());
3139     EXPECT_EQ("c.com", main_frame->ComputeSiteForCookiesForNavigation(c_url)
3140                            .registrable_domain());
3141 
3142     // a.com -> a.com frame being navigated.
3143     EXPECT_EQ("a.com", child_a->current_frame_host()
3144                            ->ComputeSiteForCookiesForNavigation(url)
3145                            .registrable_domain());
3146     EXPECT_EQ("a.com", child_a->current_frame_host()
3147                            ->ComputeSiteForCookiesForNavigation(b_url)
3148                            .registrable_domain());
3149     EXPECT_EQ("a.com", child_a->current_frame_host()
3150                            ->ComputeSiteForCookiesForNavigation(c_url)
3151                            .registrable_domain());
3152 
3153     // a.com -> a.com -> b.com frame being navigated.
3154 
3155     // The first case here is especially interesting, since we go to
3156     // a/a/a from a/a/b. We currently treat this as all first-party, but there
3157     // is a case to be made for doing it differently, due to involvement of b.
3158     EXPECT_EQ("a.com", child_b->current_frame_host()
3159                            ->ComputeSiteForCookiesForNavigation(url)
3160                            .registrable_domain());
3161     EXPECT_EQ("a.com", child_b->current_frame_host()
3162                            ->ComputeSiteForCookiesForNavigation(b_url)
3163                            .registrable_domain());
3164     EXPECT_EQ("a.com", child_b->current_frame_host()
3165                            ->ComputeSiteForCookiesForNavigation(c_url)
3166                            .registrable_domain());
3167 
3168     // a.com -> c.com frame being navigated.
3169     EXPECT_EQ("a.com", child_c->current_frame_host()
3170                            ->ComputeSiteForCookiesForNavigation(url)
3171                            .registrable_domain());
3172     EXPECT_EQ("a.com", child_c->current_frame_host()
3173                            ->ComputeSiteForCookiesForNavigation(b_url)
3174                            .registrable_domain());
3175     EXPECT_EQ("a.com", child_c->current_frame_host()
3176                            ->ComputeSiteForCookiesForNavigation(c_url)
3177                            .registrable_domain());
3178 
3179     // a.com -> a.com -> b.com -> d.com frame being navigated.
3180     EXPECT_EQ("", child_d->current_frame_host()
3181                       ->ComputeSiteForCookiesForNavigation(url)
3182                       .registrable_domain());
3183     EXPECT_EQ("", child_d->current_frame_host()
3184                       ->ComputeSiteForCookiesForNavigation(b_url)
3185                       .registrable_domain());
3186     EXPECT_EQ("", child_d->current_frame_host()
3187                       ->ComputeSiteForCookiesForNavigation(c_url)
3188                       .registrable_domain());
3189   }
3190 
3191   // Now try with a trusted scheme that gives first-partiness.
3192   GURL trusty_url(kTrustMeUrl);
3193   EXPECT_TRUE(NavigateToURL(shell(), trusty_url));
3194   {
3195     WebContentsImpl* wc =
3196         static_cast<WebContentsImpl*>(shell()->web_contents());
3197     RenderFrameHostImpl* main_frame =
3198         static_cast<RenderFrameHostImpl*>(wc->GetMainFrame());
3199     EXPECT_EQ(trusty_url.GetOrigin(),
3200               main_frame->GetLastCommittedURL().GetOrigin());
3201 
3202     ASSERT_EQ(1u, main_frame->child_count());
3203     FrameTreeNode* child_a = main_frame->child_at(0);
3204     EXPECT_EQ("a.com", child_a->current_url().host());
3205 
3206     ASSERT_EQ(2u, child_a->child_count());
3207     FrameTreeNode* child_aa = child_a->child_at(0);
3208     EXPECT_EQ("a.com", child_aa->current_url().host());
3209 
3210     ASSERT_EQ(1u, child_aa->child_count());
3211     FrameTreeNode* child_aab = child_aa->child_at(0);
3212     EXPECT_EQ("b.com", child_aab->current_url().host());
3213 
3214     ASSERT_EQ(1u, child_aab->child_count());
3215     FrameTreeNode* child_aabd = child_aab->child_at(0);
3216     EXPECT_EQ("d.com", child_aabd->current_url().host());
3217 
3218     // Main frame navigations are not affected by the special schema.
3219     EXPECT_TRUE(net::SiteForCookies::FromUrl(url).IsEquivalent(
3220         main_frame->ComputeSiteForCookiesForNavigation(url)));
3221     EXPECT_TRUE(net::SiteForCookies::FromUrl(b_url).IsEquivalent(
3222         main_frame->ComputeSiteForCookiesForNavigation(b_url)));
3223     EXPECT_TRUE(net::SiteForCookies::FromUrl(c_url).IsEquivalent(
3224         main_frame->ComputeSiteForCookiesForNavigation(c_url)));
3225 
3226     // Child navigation gets the magic scheme.
3227     EXPECT_TRUE(
3228         net::SiteForCookies::FromUrl(trusty_url)
3229             .IsEquivalent(child_aa->current_frame_host()
3230                               ->ComputeSiteForCookiesForNavigation(url)));
3231     EXPECT_TRUE(
3232         net::SiteForCookies::FromUrl(trusty_url)
3233             .IsEquivalent(child_aa->current_frame_host()
3234                               ->ComputeSiteForCookiesForNavigation(b_url)));
3235     EXPECT_TRUE(
3236         net::SiteForCookies::FromUrl(trusty_url)
3237             .IsEquivalent(child_aa->current_frame_host()
3238                               ->ComputeSiteForCookiesForNavigation(c_url)));
3239 
3240     EXPECT_TRUE(
3241         net::SiteForCookies::FromUrl(trusty_url)
3242             .IsEquivalent(child_aabd->current_frame_host()
3243                               ->ComputeSiteForCookiesForNavigation(url)));
3244     EXPECT_TRUE(
3245         net::SiteForCookies::FromUrl(trusty_url)
3246             .IsEquivalent(child_aabd->current_frame_host()
3247                               ->ComputeSiteForCookiesForNavigation(b_url)));
3248     EXPECT_TRUE(
3249         net::SiteForCookies::FromUrl(trusty_url)
3250             .IsEquivalent(child_aabd->current_frame_host()
3251                               ->ComputeSiteForCookiesForNavigation(c_url)));
3252   }
3253 
3254   // Test trusted scheme that gives first-partiness if the url is secure.
3255   GURL trusty_if_secure_url(kTrustMeIfEmbeddingSecureUrl);
3256   EXPECT_TRUE(NavigateToURL(shell(), trusty_if_secure_url));
3257   {
3258     WebContentsImpl* wc =
3259         static_cast<WebContentsImpl*>(shell()->web_contents());
3260     RenderFrameHostImpl* main_frame =
3261         static_cast<RenderFrameHostImpl*>(wc->GetMainFrame());
3262     EXPECT_EQ(trusty_if_secure_url.GetOrigin(),
3263               main_frame->GetLastCommittedURL().GetOrigin());
3264 
3265     ASSERT_EQ(1u, main_frame->child_count());
3266     FrameTreeNode* child_a = main_frame->child_at(0);
3267     EXPECT_EQ("a.com", child_a->current_url().host());
3268 
3269     ASSERT_EQ(2u, child_a->child_count());
3270     FrameTreeNode* child_aa = child_a->child_at(0);
3271     EXPECT_EQ("a.com", child_aa->current_url().host());
3272 
3273     ASSERT_EQ(1u, child_aa->child_count());
3274     FrameTreeNode* child_aab = child_aa->child_at(0);
3275     EXPECT_EQ("b.com", child_aab->current_url().host());
3276 
3277     ASSERT_EQ(1u, child_aab->child_count());
3278     FrameTreeNode* child_aabd = child_aab->child_at(0);
3279     EXPECT_EQ("d.com", child_aabd->current_url().host());
3280 
3281     // Main frame navigations are not affected by the special schema.
3282     EXPECT_TRUE(net::SiteForCookies::FromUrl(url).IsEquivalent(
3283         main_frame->ComputeSiteForCookiesForNavigation(url)));
3284     EXPECT_TRUE(net::SiteForCookies::FromUrl(b_url).IsEquivalent(
3285         main_frame->ComputeSiteForCookiesForNavigation(b_url)));
3286     EXPECT_TRUE(
3287         net::SiteForCookies::FromUrl(secure_url)
3288             .IsEquivalent(
3289                 main_frame->ComputeSiteForCookiesForNavigation(secure_url)));
3290 
3291     // Child navigation gets the magic scheme iff secure.
3292     EXPECT_TRUE(child_aa->current_frame_host()
3293                     ->ComputeSiteForCookiesForNavigation(url)
3294                     .IsNull());
3295     EXPECT_TRUE(child_aa->current_frame_host()
3296                     ->ComputeSiteForCookiesForNavigation(b_url)
3297                     .IsNull());
3298     EXPECT_TRUE(net::SiteForCookies::FromUrl(trusty_url)
3299                     .IsEquivalent(
3300                         child_aa->current_frame_host()
3301                             ->ComputeSiteForCookiesForNavigation(secure_url)));
3302 
3303     EXPECT_TRUE(child_aabd->current_frame_host()
3304                     ->ComputeSiteForCookiesForNavigation(url)
3305                     .IsNull());
3306     EXPECT_TRUE(child_aabd->current_frame_host()
3307                     ->ComputeSiteForCookiesForNavigation(b_url)
3308                     .IsNull());
3309     EXPECT_TRUE(net::SiteForCookies::FromUrl(trusty_url)
3310                     .IsEquivalent(
3311                         child_aabd->current_frame_host()
3312                             ->ComputeSiteForCookiesForNavigation(secure_url)));
3313   }
3314 
3315   SetBrowserClientForTesting(old_client);
3316 }
3317 
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,ComputeSiteForCookiesForNavigationSandbox)3318 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
3319                        ComputeSiteForCookiesForNavigationSandbox) {
3320   // Test sandboxed subframe.
3321   {
3322     GURL url = embedded_test_server()->GetURL(
3323         "a.com",
3324         "/cross_site_iframe_factory.html?a(a{sandbox-allow-scripts}(a),"
3325         "a{sandbox-allow-scripts,sandbox-allow-same-origin}(a))");
3326 
3327     EXPECT_TRUE(NavigateToURL(shell(), url));
3328 
3329     WebContentsImpl* wc =
3330         static_cast<WebContentsImpl*>(shell()->web_contents());
3331     RenderFrameHostImpl* main_frame = wc->GetMainFrame();
3332 
3333     EXPECT_EQ("a.com", main_frame->GetLastCommittedURL().host());
3334 
3335     ASSERT_EQ(2u, main_frame->child_count());
3336     FrameTreeNode* child_a = main_frame->child_at(0);
3337     EXPECT_EQ("a.com", child_a->current_url().host());
3338     EXPECT_TRUE(
3339         child_a->current_frame_host()->GetLastCommittedOrigin().opaque());
3340 
3341     ASSERT_EQ(1u, child_a->child_count());
3342     FrameTreeNode* child_aa = child_a->child_at(0);
3343     EXPECT_EQ("a.com", child_aa->current_url().host());
3344     EXPECT_TRUE(
3345         child_aa->current_frame_host()->GetLastCommittedOrigin().opaque());
3346 
3347     FrameTreeNode* child_a2 = main_frame->child_at(1);
3348     EXPECT_EQ("a.com", child_a2->current_url().host());
3349     EXPECT_FALSE(
3350         child_a2->current_frame_host()->GetLastCommittedOrigin().opaque());
3351 
3352     ASSERT_EQ(1u, child_a2->child_count());
3353     FrameTreeNode* child_a2a = child_a2->child_at(0);
3354     EXPECT_EQ("a.com", child_a2a->current_url().host());
3355     EXPECT_FALSE(
3356         child_a2a->current_frame_host()->GetLastCommittedOrigin().opaque());
3357 
3358     // |child_aa| frame navigation should be cross-site since its parent is
3359     // sandboxed without allow-same-origin
3360     EXPECT_TRUE(child_aa->current_frame_host()
3361                     ->ComputeSiteForCookiesForNavigation(url)
3362                     .IsNull());
3363 
3364     // |child_a2a| frame navigation should be same-site since its sandboxed
3365     // parent is sandbox-same-origin.
3366     EXPECT_EQ("a.com", child_a2a->current_frame_host()
3367                            ->ComputeSiteForCookiesForNavigation(url)
3368                            .registrable_domain());
3369   }
3370 
3371   // Test sandboxed main frame.
3372   {
3373     GURL url =
3374         embedded_test_server()->GetURL("a.com", "/csp_sandboxed_frame.html");
3375     EXPECT_TRUE(NavigateToURL(shell(), url));
3376 
3377     WebContentsImpl* wc =
3378         static_cast<WebContentsImpl*>(shell()->web_contents());
3379     RenderFrameHostImpl* main_frame = wc->GetMainFrame();
3380     EXPECT_EQ(url, main_frame->GetLastCommittedURL());
3381     EXPECT_TRUE(main_frame->GetLastCommittedOrigin().opaque());
3382 
3383     ASSERT_EQ(2u, main_frame->child_count());
3384     FrameTreeNode* child_a = main_frame->child_at(0);
3385     EXPECT_EQ("a.com", child_a->current_url().host());
3386     EXPECT_TRUE(
3387         child_a->current_frame_host()->GetLastCommittedOrigin().opaque());
3388 
3389     EXPECT_TRUE(child_a->current_frame_host()
3390                     ->ComputeSiteForCookiesForNavigation(url)
3391                     .IsNull());
3392   }
3393 }
3394 
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,ComputeSiteForCookiesForNavigationAboutBlank)3395 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
3396                        ComputeSiteForCookiesForNavigationAboutBlank) {
3397   GURL url = embedded_test_server()->GetURL(
3398       "a.com", "/page_with_blank_iframe_tree.html");
3399 
3400   EXPECT_TRUE(NavigateToURL(shell(), url));
3401 
3402   WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
3403   RenderFrameHostImpl* main_frame = wc->GetMainFrame();
3404 
3405   EXPECT_EQ("a.com", main_frame->GetLastCommittedURL().host());
3406 
3407   ASSERT_EQ(1u, main_frame->child_count());
3408   FrameTreeNode* child_a = main_frame->child_at(0);
3409   EXPECT_TRUE(child_a->current_url().IsAboutBlank());
3410   EXPECT_EQ("a.com",
3411             child_a->current_frame_host()->GetLastCommittedOrigin().host());
3412 
3413   ASSERT_EQ(1u, child_a->child_count());
3414   FrameTreeNode* child_aa = child_a->child_at(0);
3415   EXPECT_TRUE(child_aa->current_url().IsAboutBlank());
3416   EXPECT_EQ("a.com",
3417             child_aa->current_frame_host()->GetLastCommittedOrigin().host());
3418 
3419   // navigating the nested about:blank iframe to a.com is fine, since the origin
3420   // is inherited.
3421   EXPECT_EQ("a.com", child_aa->current_frame_host()
3422                          ->ComputeSiteForCookiesForNavigation(url)
3423                          .registrable_domain());
3424 }
3425 
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,ComputeSiteForCookiesForNavigationSrcDoc)3426 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
3427                        ComputeSiteForCookiesForNavigationSrcDoc) {
3428   // srcdoc frames basically don't figure into site_for_cookies computation.
3429   GURL url = embedded_test_server()->GetURL(
3430       "a.com", "/frame_tree/page_with_srcdoc_iframe_tree.html");
3431 
3432   EXPECT_TRUE(NavigateToURL(shell(), url));
3433 
3434   WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
3435   RenderFrameHostImpl* main_frame =
3436       static_cast<RenderFrameHostImpl*>(wc->GetMainFrame());
3437   EXPECT_EQ("a.com", main_frame->GetLastCommittedURL().host());
3438 
3439   ASSERT_EQ(1u, main_frame->child_count());
3440   FrameTreeNode* child_sd = main_frame->child_at(0);
3441   EXPECT_TRUE(child_sd->current_url().IsAboutSrcdoc());
3442 
3443   ASSERT_EQ(1u, child_sd->child_count());
3444   FrameTreeNode* child_sd_a = child_sd->child_at(0);
3445   EXPECT_EQ("a.com", child_sd_a->current_url().host());
3446 
3447   ASSERT_EQ(1u, child_sd_a->child_count());
3448   FrameTreeNode* child_sd_a_sd = child_sd_a->child_at(0);
3449   EXPECT_TRUE(child_sd_a_sd->current_url().IsAboutSrcdoc());
3450   ASSERT_EQ(0u, child_sd_a_sd->child_count());
3451 
3452   EXPECT_EQ("a.com", child_sd->current_frame_host()
3453                          ->ComputeSiteForCookiesForNavigation(url)
3454                          .registrable_domain());
3455   EXPECT_EQ("a.com", child_sd_a->current_frame_host()
3456                          ->ComputeSiteForCookiesForNavigation(url)
3457                          .registrable_domain());
3458   EXPECT_EQ("a.com", child_sd_a_sd->current_frame_host()
3459                          ->ComputeSiteForCookiesForNavigation(url)
3460                          .registrable_domain());
3461 
3462   GURL b_url = embedded_test_server()->GetURL("b.com", "/");
3463   EXPECT_EQ("b.com", main_frame->ComputeSiteForCookiesForNavigation(b_url)
3464                          .registrable_domain());
3465   EXPECT_EQ("a.com", child_sd->current_frame_host()
3466                          ->ComputeSiteForCookiesForNavigation(b_url)
3467                          .registrable_domain());
3468   EXPECT_EQ("a.com", child_sd_a->current_frame_host()
3469                          ->ComputeSiteForCookiesForNavigation(b_url)
3470                          .registrable_domain());
3471   EXPECT_EQ("a.com", child_sd_a_sd->current_frame_host()
3472                          ->ComputeSiteForCookiesForNavigation(b_url)
3473                          .registrable_domain());
3474 }
3475 
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,ComputeSiteForCookiesFileURL)3476 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
3477                        ComputeSiteForCookiesFileURL) {
3478   GURL main_frame_url = GetFileURL(FILE_PATH_LITERAL("page_with_iframe.html"));
3479   GURL subframe_url = GetFileURL(FILE_PATH_LITERAL("title1.html"));
3480   EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
3481 
3482   WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
3483   RenderFrameHostImpl* main_frame =
3484       static_cast<RenderFrameHostImpl*>(wc->GetMainFrame());
3485   EXPECT_EQ(main_frame_url, main_frame->GetLastCommittedURL());
3486   EXPECT_TRUE(net::SiteForCookies::FromUrl(GURL("file:///"))
3487                   .IsEquivalent(main_frame->ComputeSiteForCookies()));
3488 
3489   ASSERT_EQ(1u, main_frame->child_count());
3490   RenderFrameHostImpl* child = main_frame->child_at(0)->current_frame_host();
3491   EXPECT_EQ(subframe_url, child->GetLastCommittedURL());
3492   EXPECT_TRUE(net::SiteForCookies::FromUrl(GURL("file:///"))
3493                   .IsEquivalent(child->ComputeSiteForCookies()));
3494 }
3495 
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,ComputeSiteForCookiesParentNavigatedAway)3496 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
3497                        ComputeSiteForCookiesParentNavigatedAway) {
3498   // Navigate to site with same-domain frame, save a RenderFrameHostImpl to
3499   // the child.
3500   GURL url = embedded_test_server()->GetURL(
3501       "a.com", "/cross_site_iframe_factory.html?a(a)");
3502 
3503   EXPECT_TRUE(NavigateToURL(shell(), url));
3504 
3505   WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
3506   RenderFrameHostImpl* main_frame = wc->GetMainFrame();
3507 
3508   EXPECT_EQ("a.com", main_frame->GetLastCommittedURL().host());
3509 
3510   ASSERT_EQ(1u, main_frame->child_count());
3511   FrameTreeNode* child_a = main_frame->child_at(0);
3512   RenderFrameHostImpl* child_rfh = child_a->current_frame_host();
3513   EXPECT_EQ("a.com", child_rfh->GetLastCommittedOrigin().host());
3514   GURL kid_url = child_rfh->GetLastCommittedURL();
3515 
3516   // Disable the unload ACK and the unload timer. Also pretend the child frame
3517   // has an unload handler, so it doesn't get cleaned up synchronously, and
3518   // block its detach handler.
3519   auto filter = base::MakeRefCounted<DropMessageFilter>(
3520       FrameMsgStart, FrameHostMsg_Unload_ACK::ID);
3521   main_frame->GetProcess()->AddFilter(filter.get());
3522   main_frame->DisableUnloadTimerForTesting();
3523   child_rfh->SuddenTerminationDisablerChanged(
3524       true, blink::mojom::SuddenTerminationDisablerType::kUnloadHandler);
3525   child_rfh->SetSubframeUnloadTimeoutForTesting(base::TimeDelta::FromDays(7));
3526   auto filter_detach = base::MakeRefCounted<DropMessageFilter>(
3527       FrameMsgStart, FrameHostMsg_Detach::ID);
3528   child_rfh->GetProcess()->AddFilter(filter_detach.get());
3529 
3530   // Open a popup on a.com to keep the process alive.
3531   OpenPopup(shell(), embedded_test_server()->GetURL("a.com", "/title2.html"),
3532             "foo");
3533 
3534   // Navigate root to b.com.
3535   EXPECT_TRUE(NavigateToURL(
3536       shell(), embedded_test_server()->GetURL("b.com", "/title3.html")));
3537 
3538   // The old RFH should be pending deletion, but its site_for_cookies should
3539   // be unchanged.
3540   EXPECT_FALSE(child_rfh->is_active());
3541   EXPECT_EQ(kid_url, child_rfh->GetLastCommittedURL());
3542   EXPECT_EQ(url, main_frame->GetLastCommittedURL());
3543   EXPECT_FALSE(main_frame->is_active());
3544   EXPECT_FALSE(main_frame->IsCurrent());
3545   net::SiteForCookies computed_for_child = child_rfh->ComputeSiteForCookies();
3546   EXPECT_TRUE(
3547       net::SiteForCookies::FromUrl(url).IsEquivalent(computed_for_child))
3548       << computed_for_child.ToDebugString();
3549 }
3550 
3551 // Make sure a local file and its subresources can be reloaded after a crash. In
3552 // particular, after https://crbug.com/981339, a different RenderFrameHost will
3553 // be used for reloading the file. File access must be correctly granted.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,FileReloadAfterCrash)3554 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest, FileReloadAfterCrash) {
3555   WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
3556 
3557   // 1. Navigate a local file with an iframe.
3558   GURL main_frame_url = GetFileURL(FILE_PATH_LITERAL("page_with_iframe.html"));
3559   GURL subframe_url = GetFileURL(FILE_PATH_LITERAL("title1.html"));
3560   EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
3561 
3562   // 2. Crash.
3563   RenderProcessHost* process = wc->GetMainFrame()->GetProcess();
3564   RenderProcessHostWatcher crash_observer(
3565       process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
3566   process->Shutdown(0);
3567   crash_observer.Wait();
3568 
3569   // 3. Reload.
3570   wc->GetController().Reload(ReloadType::NORMAL, false);
3571   EXPECT_TRUE(WaitForLoadStop(wc));
3572 
3573   // Check the document is correctly reloaded.
3574   RenderFrameHostImpl* main_document = wc->GetMainFrame();
3575   ASSERT_EQ(1u, main_document->child_count());
3576   RenderFrameHostImpl* sub_document =
3577       main_document->child_at(0)->current_frame_host();
3578   EXPECT_EQ(main_frame_url, main_document->GetLastCommittedURL());
3579   EXPECT_EQ(subframe_url, sub_document->GetLastCommittedURL());
3580   EXPECT_EQ("\n  \n  This page has an iframe. Yay for iframes!\n  \n\n",
3581             EvalJs(main_document, "document.body.textContent"));
3582   EXPECT_EQ("This page has no title.\n\n",
3583             EvalJs(sub_document, "document.body.textContent"));
3584 }
3585 
3586 // Make sure a webui can be reloaded after a crash.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,WebUiReloadAfterCrash)3587 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest, WebUiReloadAfterCrash) {
3588   WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
3589 
3590   // 1. Navigate a local file with an iframe.
3591   GURL main_frame_url(std::string(kChromeUIScheme) + "://" +
3592                       std::string(kChromeUIGpuHost));
3593   EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
3594 
3595   // 2. Crash.
3596   RenderProcessHost* process = wc->GetMainFrame()->GetProcess();
3597   RenderProcessHostWatcher crash_observer(
3598       process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
3599   process->Shutdown(0);
3600   crash_observer.Wait();
3601 
3602   // 3. Reload.
3603   wc->GetController().Reload(ReloadType::NORMAL, false);
3604   EXPECT_TRUE(WaitForLoadStop(wc));
3605 
3606   // Check the document is correctly reloaded.
3607   RenderFrameHostImpl* main_document = wc->GetMainFrame();
3608   EXPECT_EQ(main_frame_url, main_document->GetLastCommittedURL());
3609   EXPECT_EQ("Graphics Feature Status",
3610             EvalJs(main_document, "document.querySelector('h3').textContent"));
3611 }
3612 
3613 // Start with A(B), navigate A to C. By emulating a slow unload handler B, check
3614 // the status of IsCurrent for subframes of A i.e., B before and after
3615 // navigating to C.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,CheckIsCurrentBeforeAndAfterUnload)3616 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
3617                        CheckIsCurrentBeforeAndAfterUnload) {
3618   IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
3619   std::string onunload_script = "window.onunload = function(){}";
3620   GURL url_ab(embedded_test_server()->GetURL(
3621       "a.com", "/cross_site_iframe_factory.html?a(b)"));
3622   GURL url_c(embedded_test_server()->GetURL("c.com", "/title1.html"));
3623 
3624   // 1) Navigate to a page with an iframe.
3625   EXPECT_TRUE(NavigateToURL(shell(), url_ab));
3626   RenderFrameHostImpl* rfh_a = web_contents()->GetMainFrame();
3627   RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
3628   RenderFrameDeletedObserver delete_rfh_b(rfh_b);
3629   EXPECT_EQ(RenderFrameHostImpl::UnloadState::NotRun, rfh_b->unload_state_);
3630 
3631   // 2) Set an arbitrarily long timeout to ensure the subframe unload timer
3632   // doesn't fire before we call OnDetach(). Act as if there was a slow unload
3633   // handler on rfh_b. The non navigating frames are waiting for
3634   // FrameHostMsg_Detach.
3635   rfh_b->SetSubframeUnloadTimeoutForTesting(base::TimeDelta::FromSeconds(30));
3636   auto detach_filter = base::MakeRefCounted<DropMessageFilter>(
3637       FrameMsgStart, FrameHostMsg_Detach::ID);
3638   rfh_b->GetProcess()->AddFilter(detach_filter.get());
3639   EXPECT_TRUE(ExecuteScript(rfh_b->frame_tree_node(), onunload_script));
3640 
3641   // 3) Check the IsCurrent state of rfh_a, rfh_b before navigating to C.
3642   EXPECT_TRUE(rfh_a->IsCurrent());
3643   EXPECT_TRUE(rfh_b->IsCurrent());
3644 
3645   // 4) Navigate rfh_a to C.
3646   EXPECT_TRUE(NavigateToURL(shell(), url_c));
3647   RenderFrameHostImpl* rfh_c = web_contents()->GetMainFrame();
3648   EXPECT_EQ(RenderFrameHostImpl::UnloadState::InProgress, rfh_b->unload_state_);
3649 
3650   // 5) Check the IsCurrent state of rfh_a, rfh_b and rfh_c after navigating to
3651   // C.
3652   EXPECT_FALSE(rfh_a->IsCurrent());
3653   EXPECT_FALSE(rfh_b->IsCurrent());
3654   EXPECT_TRUE(rfh_c->IsCurrent());
3655 
3656   // 6) Run detach on rfh_b to delete its frame.
3657   EXPECT_FALSE(delete_rfh_b.deleted());
3658   rfh_b->OnDetach();
3659   EXPECT_TRUE(delete_rfh_b.deleted());
3660 }
3661 
3662 namespace {
3663 
3664 // Collects the committed IPAddressSpaces, and makes them available for
3665 // evaluation. Nothing about the request is modified; this is a read-only
3666 // interceptor.
3667 class IPAddressSpaceCollector : public DidCommitNavigationInterceptor {
3668  public:
3669   using CommitData = std::pair<GURL, network::mojom::IPAddressSpace>;
3670   using CommitDataVector = std::vector<CommitData>;
3671 
IPAddressSpaceCollector(WebContents * web_contents)3672   explicit IPAddressSpaceCollector(WebContents* web_contents)
3673       : DidCommitNavigationInterceptor(web_contents) {}
3674   ~IPAddressSpaceCollector() override = default;
3675 
IPAddressSpaceForUrl(const GURL & url) const3676   network::mojom::IPAddressSpace IPAddressSpaceForUrl(const GURL& url) const {
3677     for (auto item : commits_) {
3678       if (item.first == url)
3679         return item.second;
3680     }
3681     return network::mojom::IPAddressSpace::kUnknown;
3682   }
3683 
last_ip_address_space() const3684   network::mojom::IPAddressSpace last_ip_address_space() const {
3685     return commits_.back().second;
3686   }
3687 
3688  protected:
WillProcessDidCommitNavigation(RenderFrameHost * render_frame_host,NavigationRequest * navigation_request,::FrameHostMsg_DidCommitProvisionalLoad_Params * params,mojom::DidCommitProvisionalLoadInterfaceParamsPtr * interface_params)3689   bool WillProcessDidCommitNavigation(
3690       RenderFrameHost* render_frame_host,
3691       NavigationRequest* navigation_request,
3692       ::FrameHostMsg_DidCommitProvisionalLoad_Params* params,
3693       mojom::DidCommitProvisionalLoadInterfaceParamsPtr* interface_params)
3694       override {
3695     commits_.push_back(
3696         CommitData(params->url.spec().c_str(),
3697                    navigation_request
3698                        ? navigation_request->commit_params().ip_address_space
3699                        : network::mojom::IPAddressSpace::kUnknown));
3700     return true;
3701   }
3702 
3703  private:
3704   CommitDataVector commits_;
3705 
3706   DISALLOW_COPY_AND_ASSIGN(IPAddressSpaceCollector);
3707 };
3708 
3709 }  // namespace
3710 
3711 class RenderFrameHostImplBrowserTestWithNonSecureExternalRequestsBlocked
3712     : public RenderFrameHostImplBrowserTest {
3713  public:
RenderFrameHostImplBrowserTestWithNonSecureExternalRequestsBlocked()3714   RenderFrameHostImplBrowserTestWithNonSecureExternalRequestsBlocked() {
3715     feature_list_.InitAndEnableFeature(
3716         network::features::kBlockNonSecureExternalRequests);
3717   }
3718 
3719  private:
3720   base::test::ScopedFeatureList feature_list_;
3721 };
3722 
3723 // TODO(https://crbug.com/1014325): Flaky on multiple bots.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTestWithNonSecureExternalRequestsBlocked,DISABLED_ComputeMainFrameIPAddressSpace)3724 IN_PROC_BROWSER_TEST_F(
3725     RenderFrameHostImplBrowserTestWithNonSecureExternalRequestsBlocked,
3726     DISABLED_ComputeMainFrameIPAddressSpace) {
3727   // TODO(mkwst): `about:`, `file:`, `data:`, `blob:`, and `filesystem:` URLs
3728   // are all treated as `kUnknown` today. This is ~incorrect, but safe, as their
3729   // web-facing behavior will be equivalent to "public".
3730   struct {
3731     GURL url;
3732     network::mojom::IPAddressSpace expected_internal;
3733     std::string expected_web_facing;
3734   } test_cases[] = {
3735       {GURL("about:blank"), network::mojom::IPAddressSpace::kUnknown, "public"},
3736       {GURL("data:text/html,foo"), network::mojom::IPAddressSpace::kUnknown,
3737        "public"},
3738       {GetTestUrl("", "empty.html"), network::mojom::IPAddressSpace::kUnknown,
3739        "public"},
3740       {embedded_test_server()->GetURL("/empty.html"),
3741        network::mojom::IPAddressSpace::kLocal, "local"},
3742       {embedded_test_server()->GetURL("/empty-treat-as-public-address.html"),
3743        network::mojom::IPAddressSpace::kPublic, "public"},
3744   };
3745 
3746   for (auto test : test_cases) {
3747     SCOPED_TRACE(test.url);
3748     IPAddressSpaceCollector collector(shell()->web_contents());
3749     EXPECT_TRUE(NavigateToURL(shell(), test.url));
3750     RenderFrameHostImpl* rfhi = static_cast<RenderFrameHostImpl*>(
3751         shell()->web_contents()->GetMainFrame());
3752     EXPECT_EQ(test.expected_internal, collector.last_ip_address_space());
3753     EXPECT_EQ(test.expected_web_facing, EvalJs(rfhi, "document.addressSpace"));
3754   }
3755 }
3756 
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTestWithNonSecureExternalRequestsBlocked,ComputeIFrameLoopbackIPAddressSpace)3757 IN_PROC_BROWSER_TEST_F(
3758     RenderFrameHostImplBrowserTestWithNonSecureExternalRequestsBlocked,
3759     ComputeIFrameLoopbackIPAddressSpace) {
3760   {
3761     IPAddressSpaceCollector collector(shell()->web_contents());
3762     base::string16 expected_title(base::UTF8ToUTF16("LOADED"));
3763     TitleWatcher title_watcher(shell()->web_contents(), expected_title);
3764     EXPECT_TRUE(
3765         NavigateToURL(shell(), embedded_test_server()->GetURL(
3766                                    "/do-not-treat-as-public-address.html")));
3767     EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
3768 
3769     std::vector<RenderFrameHost*> frames =
3770         shell()->web_contents()->GetAllFrames();
3771     for (auto* frame : frames) {
3772       SCOPED_TRACE(::testing::Message()
3773                    << "URL: " << frame->GetLastCommittedURL());
3774       auto* rfhi = static_cast<RenderFrameHostImpl*>(frame);
3775 
3776       if (frame->GetLastCommittedURL().IsAboutBlank()) {
3777         // TODO(986744): `about:blank` is not navigated via
3778         // `RenderFrameHostImpl::CommitNavigation`, but handled in the renderer
3779         // via `RenderFrameImpl::CommitSyncNavigation`. This means that we don't
3780         // calculate the value correctly on the browser-side, but do correctly
3781         // inherit from the initiator on the Blink-side.
3782         EXPECT_EQ(network::mojom::IPAddressSpace::kUnknown,
3783                   collector.IPAddressSpaceForUrl(frame->GetLastCommittedURL()));
3784         EXPECT_EQ("local", EvalJs(rfhi, "document.addressSpace"));
3785       } else if (frame->GetLastCommittedURL().SchemeIsFileSystem() ||
3786                  frame->GetLastCommittedURL().SchemeIsBlob() ||
3787                  frame->GetLastCommittedURL().IsAboutSrcdoc() ||
3788                  frame->GetLastCommittedURL().SchemeIs(url::kDataScheme)) {
3789         // TODO(986744): `data:`, `blob:`, `filesystem:`, and `about:srcdoc`
3790         // should all inherit the IPAddressSpace from the document
3791         // that initiated a navigation. Right now, we treat them as `kPublic`.
3792         EXPECT_EQ(network::mojom::IPAddressSpace::kUnknown,
3793                   collector.IPAddressSpaceForUrl(frame->GetLastCommittedURL()));
3794         EXPECT_EQ("public", EvalJs(rfhi, "document.addressSpace"));
3795       } else {
3796         // TODO(mkwst): Once the above two TODOs are resolved, this branch will
3797         // be the correct expectation for all the frames in this test.
3798         EXPECT_EQ(network::mojom::IPAddressSpace::kLocal,
3799                   collector.IPAddressSpaceForUrl(frame->GetLastCommittedURL()));
3800         EXPECT_EQ("local", EvalJs(rfhi, "document.addressSpace"));
3801       }
3802     }
3803   }
3804 
3805   // Loading from loopback that asserts publicness: `data:`, `blob:`,
3806   // `filesystem:`, `about:blank`, and `about:srcdoc` all inherit the assertion.
3807   {
3808     IPAddressSpaceCollector collector(shell()->web_contents());
3809     base::string16 expected_title(base::UTF8ToUTF16("LOADED"));
3810     TitleWatcher title_watcher(shell()->web_contents(), expected_title);
3811     EXPECT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL(
3812                                            "/treat-as-public-address.html")));
3813     EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
3814 
3815     std::vector<RenderFrameHost*> frames =
3816         shell()->web_contents()->GetAllFrames();
3817     for (auto* frame : frames) {
3818       auto* rfhi = static_cast<RenderFrameHostImpl*>(frame);
3819       if (frame->GetLastCommittedURL().IsAboutBlank()) {
3820         // TODO(986744): `about:blank` is not navigated via `NavigationRequest`,
3821         // but handled in the renderer via
3822         // `RenderFrameImpl::CommitSyncNavigation`. This means that we don't
3823         // calculate the value correctly on the browser-side, but do correctly
3824         // inherit from the initiator on the Blink-side.
3825         EXPECT_EQ(network::mojom::IPAddressSpace::kUnknown,
3826                   collector.IPAddressSpaceForUrl(frame->GetLastCommittedURL()));
3827         EXPECT_EQ("public", EvalJs(rfhi, "document.addressSpace"));
3828       } else if (frame->GetLastCommittedURL().SchemeIsFileSystem() ||
3829                  frame->GetLastCommittedURL().SchemeIsBlob() ||
3830                  frame->GetLastCommittedURL().IsAboutSrcdoc() ||
3831                  frame->GetLastCommittedURL().SchemeIs(url::kDataScheme)) {
3832         // TODO(986744): `data:`, `blob:`, `filesystem:`, and `about:srcdoc`
3833         // should all inherit the IPAddressSpace from the document
3834         // that initiated a navigation. Right now, we treat them as `kUnknown`.
3835         EXPECT_EQ(network::mojom::IPAddressSpace::kUnknown,
3836                   collector.IPAddressSpaceForUrl(frame->GetLastCommittedURL()));
3837         EXPECT_EQ("public", EvalJs(rfhi, "document.addressSpace"));
3838       } else {
3839         EXPECT_EQ(network::mojom::IPAddressSpace::kPublic,
3840                   collector.IPAddressSpaceForUrl(frame->GetLastCommittedURL()));
3841         EXPECT_EQ("public", EvalJs(rfhi, "document.addressSpace"));
3842       }
3843     }
3844   }
3845 }
3846 
3847 namespace {
3848 
3849 // Calls |callback| whenever a DOMContentLoaded is reached in
3850 // |render_frame_host|.
3851 class DOMContentLoadedObserver : public WebContentsObserver {
3852  public:
DOMContentLoadedObserver(WebContents * web_contents,base::RepeatingClosure callback)3853   DOMContentLoadedObserver(WebContents* web_contents,
3854                            base::RepeatingClosure callback)
3855       : WebContentsObserver(web_contents), callback_(callback) {}
3856 
3857  protected:
3858   // WebContentsObserver:
DOMContentLoaded(RenderFrameHost * render_Frame_host)3859   void DOMContentLoaded(RenderFrameHost* render_Frame_host) override {
3860     callback_.Run();
3861   }
3862 
3863  private:
3864   base::RepeatingClosure callback_;
3865   DISALLOW_COPY_AND_ASSIGN(DOMContentLoadedObserver);
3866 };
3867 
3868 // Calls |callback| whenever a DocumentOnLoad is reached in
3869 // |render_frame_host|.
3870 class DocumentOnLoadObserver : public WebContentsObserver {
3871  public:
DocumentOnLoadObserver(WebContents * web_contents,base::RepeatingClosure callback)3872   DocumentOnLoadObserver(WebContents* web_contents,
3873                          base::RepeatingClosure callback)
3874       : WebContentsObserver(web_contents), callback_(callback) {}
3875 
3876  protected:
3877   // WebContentsObserver:
DocumentOnLoadCompletedInMainFrame()3878   void DocumentOnLoadCompletedInMainFrame() override { callback_.Run(); }
3879 
3880  private:
3881   base::RepeatingClosure callback_;
3882   DISALLOW_COPY_AND_ASSIGN(DocumentOnLoadObserver);
3883 };
3884 
3885 }  // namespace
3886 
IN_PROC_BROWSER_TEST_F(ContentBrowserTest,LoadCallbacks)3887 IN_PROC_BROWSER_TEST_F(ContentBrowserTest, LoadCallbacks) {
3888   net::test_server::ControllableHttpResponse main_document_response(
3889       embedded_test_server(), "/main_document");
3890   net::test_server::ControllableHttpResponse image_response(
3891       embedded_test_server(), "/img");
3892 
3893   EXPECT_TRUE(embedded_test_server()->Start());
3894   GURL main_document_url(embedded_test_server()->GetURL("/main_document"));
3895 
3896   WebContents* web_contents = shell()->web_contents();
3897   RenderFrameHostImpl* rfhi =
3898       static_cast<RenderFrameHostImpl*>(web_contents->GetMainFrame());
3899   TestNavigationObserver load_observer(web_contents);
3900   base::RunLoop loop_until_dcl;
3901   DOMContentLoadedObserver dcl_observer(web_contents,
3902                                         loop_until_dcl.QuitClosure());
3903   shell()->LoadURL(main_document_url);
3904 
3905   EXPECT_FALSE(rfhi->IsDOMContentLoaded());
3906   EXPECT_FALSE(web_contents->IsDocumentOnLoadCompletedInMainFrame());
3907 
3908   main_document_response.WaitForRequest();
3909   main_document_response.Send(
3910       "HTTP/1.1 200 OK\r\n"
3911       "Connection: close\r\n"
3912       "Content-Type: text/html; charset=utf-8\r\n"
3913       "\r\n"
3914       "<img src='/img'>");
3915 
3916   load_observer.WaitForNavigationFinished();
3917   EXPECT_FALSE(rfhi->IsDOMContentLoaded());
3918   EXPECT_FALSE(web_contents->IsDocumentOnLoadCompletedInMainFrame());
3919 
3920   main_document_response.Done();
3921 
3922   // We should reach DOMContentLoaded, but not onload, since the image resource
3923   // is still loading.
3924   loop_until_dcl.Run();
3925   EXPECT_TRUE(rfhi->is_loading());
3926   EXPECT_TRUE(rfhi->IsDOMContentLoaded());
3927   EXPECT_FALSE(web_contents->IsDocumentOnLoadCompletedInMainFrame());
3928 
3929   base::RunLoop loop_until_onload;
3930   DocumentOnLoadObserver onload_observer(web_contents,
3931                                          loop_until_onload.QuitClosure());
3932 
3933   image_response.WaitForRequest();
3934   image_response.Done();
3935 
3936   // And now onload() should be reached.
3937   loop_until_onload.Run();
3938   EXPECT_TRUE(rfhi->IsDOMContentLoaded());
3939   EXPECT_TRUE(web_contents->IsDocumentOnLoadCompletedInMainFrame());
3940 }
3941 
IN_PROC_BROWSER_TEST_F(ContentBrowserTest,LoadingStateResetOnNavigation)3942 IN_PROC_BROWSER_TEST_F(ContentBrowserTest, LoadingStateResetOnNavigation) {
3943   net::test_server::ControllableHttpResponse document2_response(
3944       embedded_test_server(), "/document2");
3945 
3946   EXPECT_TRUE(embedded_test_server()->Start());
3947   GURL url1(embedded_test_server()->GetURL("/title1.html"));
3948   GURL url2(embedded_test_server()->GetURL("/document2"));
3949 
3950   WebContents* web_contents = shell()->web_contents();
3951   RenderFrameHostImpl* rfhi =
3952       static_cast<RenderFrameHostImpl*>(web_contents->GetMainFrame());
3953 
3954   base::RunLoop loop_until_onload;
3955   DocumentOnLoadObserver onload_observer(web_contents,
3956                                          loop_until_onload.QuitClosure());
3957   shell()->LoadURL(url1);
3958   loop_until_onload.Run();
3959 
3960   EXPECT_TRUE(rfhi->IsDOMContentLoaded());
3961   EXPECT_TRUE(web_contents->IsDocumentOnLoadCompletedInMainFrame());
3962 
3963   // Expect that the loading state will be reset after a navigation.
3964 
3965   TestNavigationObserver navigation_observer(web_contents);
3966   shell()->LoadURL(url2);
3967 
3968   document2_response.WaitForRequest();
3969   document2_response.Send(
3970       "HTTP/1.1 200 OK\r\n"
3971       "Content-Type: text/html; charset=utf-8\r\n"
3972       "\r\n");
3973   navigation_observer.WaitForNavigationFinished();
3974 
3975   EXPECT_FALSE(rfhi->IsDOMContentLoaded());
3976   EXPECT_FALSE(web_contents->IsDocumentOnLoadCompletedInMainFrame());
3977 }
3978 
IN_PROC_BROWSER_TEST_F(ContentBrowserTest,LoadingStateIsNotResetOnFailedNavigation)3979 IN_PROC_BROWSER_TEST_F(ContentBrowserTest,
3980                        LoadingStateIsNotResetOnFailedNavigation) {
3981   net::test_server::ControllableHttpResponse document2_response(
3982       embedded_test_server(), "/document2");
3983 
3984   EXPECT_TRUE(embedded_test_server()->Start());
3985   GURL url1(embedded_test_server()->GetURL("/title1.html"));
3986   GURL url2(embedded_test_server()->GetURL("/document2"));
3987 
3988   WebContents* web_contents = shell()->web_contents();
3989   RenderFrameHostImpl* rfhi =
3990       static_cast<RenderFrameHostImpl*>(web_contents->GetMainFrame());
3991 
3992   base::RunLoop loop_until_onload;
3993   DocumentOnLoadObserver onload_observer(web_contents,
3994                                          loop_until_onload.QuitClosure());
3995   shell()->LoadURL(url1);
3996   loop_until_onload.Run();
3997 
3998   EXPECT_TRUE(rfhi->IsDOMContentLoaded());
3999   EXPECT_TRUE(web_contents->IsDocumentOnLoadCompletedInMainFrame());
4000 
4001   // Expect that the loading state will NOT be reset after a cancelled
4002   // navigation.
4003 
4004   TestNavigationManager navigation_manager(web_contents, url2);
4005   shell()->LoadURL(url2);
4006   EXPECT_TRUE(navigation_manager.WaitForRequestStart());
4007   navigation_manager.ResumeNavigation();
4008   document2_response.WaitForRequest();
4009 
4010   document2_response.Send(
4011       "HTTP/1.1 204 No Content\r\n"
4012       "Content-Type: text/html; charset=utf-8\r\n"
4013       "\r\n");
4014   navigation_manager.WaitForNavigationFinished();
4015 
4016   EXPECT_TRUE(rfhi->IsDOMContentLoaded());
4017   EXPECT_TRUE(web_contents->IsDocumentOnLoadCompletedInMainFrame());
4018 }
4019 
4020 // TODO(crbug.com/794320): the code below is temporary and will be removed when
4021 // Java Bridge is mojofied.
4022 #if defined(OS_ANDROID)
4023 
4024 struct ObjectData {
4025   const int32_t id;
4026   const std::vector<std::string> methods;
4027 };
4028 
4029 ObjectData kMainObject{5, {"getId", "getInnerObject", "readArray"}};
4030 ObjectData kInnerObject{10, {"getInnerId"}};
4031 
4032 class MockInnerObject : public blink::mojom::RemoteObject {
4033  public:
HasMethod(const std::string & name,HasMethodCallback callback)4034   void HasMethod(const std::string& name, HasMethodCallback callback) override {
4035     bool has_method =
4036         std::find(kInnerObject.methods.begin(), kInnerObject.methods.end(),
4037                   name) != kInnerObject.methods.end();
4038     std::move(callback).Run(has_method);
4039   }
GetMethods(GetMethodsCallback callback)4040   void GetMethods(GetMethodsCallback callback) override {
4041     std::move(callback).Run(kInnerObject.methods);
4042   }
InvokeMethod(const std::string & name,std::vector<blink::mojom::RemoteInvocationArgumentPtr> arguments,InvokeMethodCallback callback)4043   void InvokeMethod(
4044       const std::string& name,
4045       std::vector<blink::mojom::RemoteInvocationArgumentPtr> arguments,
4046       InvokeMethodCallback callback) override {
4047     EXPECT_EQ("getInnerId", name);
4048     blink::mojom::RemoteInvocationResultPtr result =
4049         blink::mojom::RemoteInvocationResult::New();
4050     result->error = blink::mojom::RemoteInvocationError::OK;
4051     result->value = blink::mojom::RemoteInvocationResultValue::NewNumberValue(
4052         kInnerObject.id);
4053     std::move(callback).Run(std::move(result));
4054   }
4055 };
4056 
4057 class MockObject : public blink::mojom::RemoteObject {
4058  public:
MockObject(mojo::PendingReceiver<blink::mojom::RemoteObject> receiver)4059   explicit MockObject(
4060       mojo::PendingReceiver<blink::mojom::RemoteObject> receiver)
4061       : receiver_(this, std::move(receiver)) {}
HasMethod(const std::string & name,HasMethodCallback callback)4062   void HasMethod(const std::string& name, HasMethodCallback callback) override {
4063     bool has_method =
4064         std::find(kMainObject.methods.begin(), kMainObject.methods.end(),
4065                   name) != kMainObject.methods.end();
4066     std::move(callback).Run(has_method);
4067   }
4068 
GetMethods(GetMethodsCallback callback)4069   void GetMethods(GetMethodsCallback callback) override {
4070     std::move(callback).Run(kMainObject.methods);
4071   }
InvokeMethod(const std::string & name,std::vector<blink::mojom::RemoteInvocationArgumentPtr> arguments,InvokeMethodCallback callback)4072   void InvokeMethod(
4073       const std::string& name,
4074       std::vector<blink::mojom::RemoteInvocationArgumentPtr> arguments,
4075       InvokeMethodCallback callback) override {
4076     blink::mojom::RemoteInvocationResultPtr result =
4077         blink::mojom::RemoteInvocationResult::New();
4078     result->error = blink::mojom::RemoteInvocationError::OK;
4079     if (name == "getId") {
4080       result->value = blink::mojom::RemoteInvocationResultValue::NewNumberValue(
4081           kMainObject.id);
4082     } else if (name == "readArray") {
4083       EXPECT_EQ(1U, arguments.size());
4084       EXPECT_TRUE(arguments[0]->is_array_value());
4085       num_elements_received_ = arguments[0]->get_array_value().size();
4086       result->value =
4087           blink::mojom::RemoteInvocationResultValue::NewBooleanValue(true);
4088     } else if (name == "getInnerObject") {
4089       result->value = blink::mojom::RemoteInvocationResultValue::NewObjectId(
4090           kInnerObject.id);
4091     }
4092     std::move(callback).Run(std::move(result));
4093   }
4094 
get_num_elements_received() const4095   int get_num_elements_received() const { return num_elements_received_; }
4096 
4097  private:
4098   int num_elements_received_ = 0;
4099   mojo::Receiver<blink::mojom::RemoteObject> receiver_;
4100 };
4101 
4102 class MockObjectHost : public blink::mojom::RemoteObjectHost {
4103  public:
GetObject(int32_t object_id,mojo::PendingReceiver<blink::mojom::RemoteObject> receiver)4104   void GetObject(
4105       int32_t object_id,
4106       mojo::PendingReceiver<blink::mojom::RemoteObject> receiver) override {
4107     if (object_id == kMainObject.id) {
4108       mock_object_ = std::make_unique<MockObject>(std::move(receiver));
4109     } else if (object_id == kInnerObject.id) {
4110       mojo::MakeSelfOwnedReceiver(std::make_unique<MockInnerObject>(),
4111                                   std::move(receiver));
4112     }
4113   }
4114 
ReleaseObject(int32_t)4115   void ReleaseObject(int32_t) override {
4116     // TODO(crbug.com/794320): implement this.
4117   }
4118 
GetRemote()4119   mojo::PendingRemote<blink::mojom::RemoteObjectHost> GetRemote() {
4120     return receiver_.BindNewPipeAndPassRemote();
4121   }
4122 
GetMockObject() const4123   MockObject* GetMockObject() const { return mock_object_.get(); }
4124 
4125  private:
4126   mojo::Receiver<blink::mojom::RemoteObjectHost> receiver_{this};
4127   std::unique_ptr<MockObject> mock_object_;
4128 };
4129 
4130 class RemoteObjectInjector : public WebContentsObserver {
4131  public:
RemoteObjectInjector(WebContents * web_contents)4132   explicit RemoteObjectInjector(WebContents* web_contents)
4133       : WebContentsObserver(web_contents) {}
4134 
GetObjectHost() const4135   const MockObjectHost& GetObjectHost() const { return host_; }
4136 
4137  private:
RenderFrameCreated(RenderFrameHost * render_frame_host)4138   void RenderFrameCreated(RenderFrameHost* render_frame_host) override {
4139     mojo::Remote<blink::mojom::RemoteObjectGateway> gateway;
4140     mojo::Remote<blink::mojom::RemoteObjectGatewayFactory> factory;
4141     static_cast<RenderFrameHostImpl*>(render_frame_host)
4142         ->GetRemoteInterfaces()
4143         ->GetInterface(factory.BindNewPipeAndPassReceiver());
4144     factory->CreateRemoteObjectGateway(host_.GetRemote(),
4145                                        gateway.BindNewPipeAndPassReceiver());
4146     gateway->AddNamedObject("testObject", kMainObject.id);
4147   }
4148 
4149   MockObjectHost host_;
4150 
4151   DISALLOW_COPY_AND_ASSIGN(RemoteObjectInjector);
4152 };
4153 
4154 namespace {
SetupRemoteObjectInvocation(Shell * shell,const GURL & url)4155 void SetupRemoteObjectInvocation(Shell* shell, const GURL& url) {
4156   WebContents* web_contents = shell->web_contents();
4157 
4158   // The first load triggers RenderFrameCreated on a RenderFrameHostObserver
4159   // instance, where the object injection happens.
4160   shell->LoadURL(url);
4161   EXPECT_TRUE(WaitForLoadStop(web_contents));
4162   // Injected objects become visible only after reload.
4163   web_contents->GetController().Reload(ReloadType::NORMAL, false);
4164   EXPECT_TRUE(WaitForLoadStop(web_contents));
4165 }
4166 }  // namespace
4167 
4168 // TODO(crbug.com/794320): Remove this when the new Java Bridge code is
4169 // integrated into WebView.
4170 // This test is a temporary way of verifying that the renderer part
4171 // works as expected.
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,RemoteObjectEnumerateProperties)4172 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
4173                        RemoteObjectEnumerateProperties) {
4174   GURL url(embedded_test_server()->GetURL("/empty.html"));
4175 
4176   WebContents* web_contents = shell()->web_contents();
4177   RemoteObjectInjector injector(web_contents);
4178   SetupRemoteObjectInvocation(shell(), url);
4179 
4180   std::string kScript = "Object.keys(testObject).join(' ');";
4181   auto result = EvalJs(web_contents, kScript);
4182   EXPECT_EQ(base::JoinString(kMainObject.methods, " "),
4183             result.value.GetString());
4184 }
4185 
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,RemoteObjectInvokeNonexistentMethod)4186 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
4187                        RemoteObjectInvokeNonexistentMethod) {
4188   GURL url(embedded_test_server()->GetURL("/empty.html"));
4189 
4190   WebContents* web_contents = shell()->web_contents();
4191   RemoteObjectInjector injector(web_contents);
4192   SetupRemoteObjectInvocation(shell(), url);
4193 
4194   std::string kScript = "testObject.getInnerId();";
4195   EXPECT_FALSE(EvalJs(web_contents, kScript).error.empty());
4196 }
4197 
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,RemoteObjectInvokeMethodReturningNumber)4198 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
4199                        RemoteObjectInvokeMethodReturningNumber) {
4200   GURL url(embedded_test_server()->GetURL("/empty.html"));
4201 
4202   WebContents* web_contents = shell()->web_contents();
4203   RemoteObjectInjector injector(web_contents);
4204   SetupRemoteObjectInvocation(shell(), url);
4205 
4206   std::string kScript = "testObject.getId();";
4207   EXPECT_EQ(kMainObject.id, EvalJs(web_contents, kScript));
4208 }
4209 
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,RemoteObjectInvokeMethodTakingArray)4210 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
4211                        RemoteObjectInvokeMethodTakingArray) {
4212   GURL url(embedded_test_server()->GetURL("/empty.html"));
4213 
4214   WebContents* web_contents = shell()->web_contents();
4215   RemoteObjectInjector injector(web_contents);
4216   SetupRemoteObjectInvocation(shell(), url);
4217 
4218   std::string kScript = "testObject.readArray([6, 8, 2]);";
4219   EXPECT_TRUE(EvalJs(web_contents, kScript).error.empty());
4220   EXPECT_EQ(
4221       3, injector.GetObjectHost().GetMockObject()->get_num_elements_received());
4222 }
4223 
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,RemoteObjectInvokeMethodReturningObject)4224 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
4225                        RemoteObjectInvokeMethodReturningObject) {
4226   GURL url(embedded_test_server()->GetURL("/empty.html"));
4227 
4228   WebContents* web_contents = shell()->web_contents();
4229   RemoteObjectInjector injector(web_contents);
4230   SetupRemoteObjectInvocation(shell(), url);
4231 
4232   std::string kScript = "testObject.getInnerObject().getInnerId();";
4233   EXPECT_EQ(kInnerObject.id, EvalJs(web_contents, kScript));
4234 }
4235 
IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,RemoteObjectInvokeMethodException)4236 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
4237                        RemoteObjectInvokeMethodException) {
4238   GURL url(embedded_test_server()->GetURL("/empty.html"));
4239 
4240   WebContents* web_contents = shell()->web_contents();
4241   RemoteObjectInjector injector(web_contents);
4242   SetupRemoteObjectInvocation(shell(), url);
4243 
4244   std::string error_message = "hahaha";
4245 
4246   std::string kScript = JsReplace(R"(
4247       const array = [1, 2, 3];
4248       Object.defineProperty(array, 0, {
4249         get() { throw new Error($1); }
4250       });
4251       testObject.readArray(array);
4252     )",
4253                                   error_message);
4254   auto error = EvalJs(web_contents, kScript).error;
4255   EXPECT_NE(error.find(error_message), std::string::npos);
4256 }
4257 
4258 #endif  // OS_ANDROID
4259 }  // namespace content
4260