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