render_frame_host_manager_browsertest.cc revision effb81e5f8246d0db0270817048dc992db66e9fb
1// Copyright 2013 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 <set>
6
7#include "base/command_line.h"
8#include "base/json/json_reader.h"
9#include "base/memory/ref_counted.h"
10#include "base/path_service.h"
11#include "base/strings/utf_string_conversions.h"
12#include "base/values.h"
13#include "content/browser/child_process_security_policy_impl.h"
14#include "content/browser/renderer_host/render_view_host_impl.h"
15#include "content/browser/site_instance_impl.h"
16#include "content/browser/web_contents/web_contents_impl.h"
17#include "content/browser/webui/web_ui_impl.h"
18#include "content/common/content_constants_internal.h"
19#include "content/public/browser/navigation_controller.h"
20#include "content/public/browser/navigation_entry.h"
21#include "content/public/browser/render_process_host.h"
22#include "content/public/browser/web_contents.h"
23#include "content/public/browser/web_contents_observer.h"
24#include "content/public/common/content_switches.h"
25#include "content/public/common/url_constants.h"
26#include "content/public/test/browser_test_utils.h"
27#include "content/public/test/content_browser_test.h"
28#include "content/public/test/content_browser_test_utils.h"
29#include "content/public/test/test_navigation_observer.h"
30#include "content/public/test/test_utils.h"
31#include "content/shell/browser/shell.h"
32#include "net/base/net_util.h"
33#include "net/dns/mock_host_resolver.h"
34#include "net/test/spawned_test_server/spawned_test_server.h"
35
36using base::ASCIIToUTF16;
37
38namespace content {
39
40class RenderFrameHostManagerTest : public ContentBrowserTest {
41 public:
42  RenderFrameHostManagerTest() : foo_com_("foo.com") {
43    replace_host_.SetHostStr(foo_com_);
44  }
45
46  static bool GetFilePathWithHostAndPortReplacement(
47      const std::string& original_file_path,
48      const net::HostPortPair& host_port_pair,
49      std::string* replacement_path) {
50    std::vector<net::SpawnedTestServer::StringPair> replacement_text;
51    replacement_text.push_back(
52        make_pair("REPLACE_WITH_HOST_AND_PORT", host_port_pair.ToString()));
53    return net::SpawnedTestServer::GetFilePathWithReplacements(
54        original_file_path, replacement_text, replacement_path);
55  }
56
57  void StartServer() {
58    // Support multiple sites on the test server.
59    host_resolver()->AddRule("*", "127.0.0.1");
60    ASSERT_TRUE(test_server()->Start());
61
62    foo_host_port_ = test_server()->host_port_pair();
63    foo_host_port_.set_host(foo_com_);
64  }
65
66  // Returns a URL on foo.com with the given path.
67  GURL GetCrossSiteURL(const std::string& path) {
68    GURL cross_site_url(test_server()->GetURL(path));
69    return cross_site_url.ReplaceComponents(replace_host_);
70  }
71
72 protected:
73  std::string foo_com_;
74  GURL::Replacements replace_host_;
75  net::HostPortPair foo_host_port_;
76};
77
78// Web pages should not have script access to the swapped out page.
79IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, NoScriptAccessAfterSwapOut) {
80  StartServer();
81
82  // Load a page with links that open in a new window.
83  std::string replacement_path;
84  ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
85      "files/click-noreferrer-links.html",
86      foo_host_port_,
87      &replacement_path));
88  NavigateToURL(shell(), test_server()->GetURL(replacement_path));
89
90  // Get the original SiteInstance for later comparison.
91  scoped_refptr<SiteInstance> orig_site_instance(
92      shell()->web_contents()->GetSiteInstance());
93  EXPECT_TRUE(orig_site_instance.get() != NULL);
94
95  // Open a same-site link in a new window.
96  ShellAddedObserver new_shell_observer;
97  bool success = false;
98  EXPECT_TRUE(ExecuteScriptAndExtractBool(
99      shell()->web_contents(),
100      "window.domAutomationController.send(clickSameSiteTargetedLink());",
101      &success));
102  EXPECT_TRUE(success);
103  Shell* new_shell = new_shell_observer.GetShell();
104
105  // Wait for the navigation in the new window to finish, if it hasn't.
106  WaitForLoadStop(new_shell->web_contents());
107  EXPECT_EQ("/files/navigate_opener.html",
108            new_shell->web_contents()->GetLastCommittedURL().path());
109
110  // Should have the same SiteInstance.
111  scoped_refptr<SiteInstance> blank_site_instance(
112      new_shell->web_contents()->GetSiteInstance());
113  EXPECT_EQ(orig_site_instance, blank_site_instance);
114
115  // We should have access to the opened window's location.
116  success = false;
117  EXPECT_TRUE(ExecuteScriptAndExtractBool(
118      shell()->web_contents(),
119      "window.domAutomationController.send(testScriptAccessToWindow());",
120      &success));
121  EXPECT_TRUE(success);
122
123  // Now navigate the new window to a different site.
124  NavigateToURL(new_shell, GetCrossSiteURL("files/title1.html"));
125  scoped_refptr<SiteInstance> new_site_instance(
126      new_shell->web_contents()->GetSiteInstance());
127  EXPECT_NE(orig_site_instance, new_site_instance);
128
129  // We should no longer have script access to the opened window's location.
130  success = false;
131  EXPECT_TRUE(ExecuteScriptAndExtractBool(
132      shell()->web_contents(),
133      "window.domAutomationController.send(testScriptAccessToWindow());",
134      &success));
135  EXPECT_FALSE(success);
136}
137
138// Test for crbug.com/24447.  Following a cross-site link with rel=noreferrer
139// and target=_blank should create a new SiteInstance.
140IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
141                       SwapProcessWithRelNoreferrerAndTargetBlank) {
142  StartServer();
143
144  // Load a page with links that open in a new window.
145  std::string replacement_path;
146  ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
147      "files/click-noreferrer-links.html",
148      foo_host_port_,
149      &replacement_path));
150  NavigateToURL(shell(), test_server()->GetURL(replacement_path));
151
152  // Get the original SiteInstance for later comparison.
153  scoped_refptr<SiteInstance> orig_site_instance(
154      shell()->web_contents()->GetSiteInstance());
155  EXPECT_TRUE(orig_site_instance.get() != NULL);
156
157  // Test clicking a rel=noreferrer + target=blank link.
158  ShellAddedObserver new_shell_observer;
159  bool success = false;
160  EXPECT_TRUE(ExecuteScriptAndExtractBool(
161      shell()->web_contents(),
162      "window.domAutomationController.send(clickNoRefTargetBlankLink());",
163      &success));
164  EXPECT_TRUE(success);
165
166  // Wait for the window to open.
167  Shell* new_shell = new_shell_observer.GetShell();
168
169  EXPECT_EQ("/files/title2.html",
170            new_shell->web_contents()->GetVisibleURL().path());
171
172  // Wait for the cross-site transition in the new tab to finish.
173  WaitForLoadStop(new_shell->web_contents());
174  WebContentsImpl* web_contents = static_cast<WebContentsImpl*>(
175      new_shell->web_contents());
176  EXPECT_FALSE(web_contents->GetRenderManagerForTesting()->
177      pending_render_view_host());
178
179  // Should have a new SiteInstance.
180  scoped_refptr<SiteInstance> noref_blank_site_instance(
181      new_shell->web_contents()->GetSiteInstance());
182  EXPECT_NE(orig_site_instance, noref_blank_site_instance);
183}
184
185// As of crbug.com/69267, we create a new BrowsingInstance (and SiteInstance)
186// for rel=noreferrer links in new windows, even to same site pages and named
187// targets.
188IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
189                       SwapProcessWithSameSiteRelNoreferrer) {
190  StartServer();
191
192  // Load a page with links that open in a new window.
193  std::string replacement_path;
194  ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
195      "files/click-noreferrer-links.html",
196      foo_host_port_,
197      &replacement_path));
198  NavigateToURL(shell(), test_server()->GetURL(replacement_path));
199
200  // Get the original SiteInstance for later comparison.
201  scoped_refptr<SiteInstance> orig_site_instance(
202      shell()->web_contents()->GetSiteInstance());
203  EXPECT_TRUE(orig_site_instance.get() != NULL);
204
205  // Test clicking a same-site rel=noreferrer + target=foo link.
206  ShellAddedObserver new_shell_observer;
207  bool success = false;
208  EXPECT_TRUE(ExecuteScriptAndExtractBool(
209      shell()->web_contents(),
210      "window.domAutomationController.send(clickSameSiteNoRefTargetedLink());",
211      &success));
212  EXPECT_TRUE(success);
213
214  // Wait for the window to open.
215  Shell* new_shell = new_shell_observer.GetShell();
216
217  // Opens in new window.
218  EXPECT_EQ("/files/title2.html",
219            new_shell->web_contents()->GetVisibleURL().path());
220
221  // Wait for the cross-site transition in the new tab to finish.
222  WaitForLoadStop(new_shell->web_contents());
223  WebContentsImpl* web_contents = static_cast<WebContentsImpl*>(
224      new_shell->web_contents());
225  EXPECT_FALSE(web_contents->GetRenderManagerForTesting()->
226      pending_render_view_host());
227
228  // Should have a new SiteInstance (in a new BrowsingInstance).
229  scoped_refptr<SiteInstance> noref_blank_site_instance(
230      new_shell->web_contents()->GetSiteInstance());
231  EXPECT_NE(orig_site_instance, noref_blank_site_instance);
232}
233
234// Test for crbug.com/24447.  Following a cross-site link with just
235// target=_blank should not create a new SiteInstance.
236IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
237                       DontSwapProcessWithOnlyTargetBlank) {
238  StartServer();
239
240  // Load a page with links that open in a new window.
241  std::string replacement_path;
242  ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
243      "files/click-noreferrer-links.html",
244      foo_host_port_,
245      &replacement_path));
246  NavigateToURL(shell(), test_server()->GetURL(replacement_path));
247
248  // Get the original SiteInstance for later comparison.
249  scoped_refptr<SiteInstance> orig_site_instance(
250      shell()->web_contents()->GetSiteInstance());
251  EXPECT_TRUE(orig_site_instance.get() != NULL);
252
253  // Test clicking a target=blank link.
254  ShellAddedObserver new_shell_observer;
255  bool success = false;
256  EXPECT_TRUE(ExecuteScriptAndExtractBool(
257      shell()->web_contents(),
258      "window.domAutomationController.send(clickTargetBlankLink());",
259      &success));
260  EXPECT_TRUE(success);
261
262  // Wait for the window to open.
263  Shell* new_shell = new_shell_observer.GetShell();
264
265  // Wait for the cross-site transition in the new tab to finish.
266  WaitForLoadStop(new_shell->web_contents());
267  EXPECT_EQ("/files/title2.html",
268            new_shell->web_contents()->GetLastCommittedURL().path());
269
270  // Should have the same SiteInstance.
271  scoped_refptr<SiteInstance> blank_site_instance(
272      new_shell->web_contents()->GetSiteInstance());
273  EXPECT_EQ(orig_site_instance, blank_site_instance);
274}
275
276// Test for crbug.com/24447.  Following a cross-site link with rel=noreferrer
277// and no target=_blank should not create a new SiteInstance.
278IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
279                       DontSwapProcessWithOnlyRelNoreferrer) {
280  StartServer();
281
282  // Load a page with links that open in a new window.
283  std::string replacement_path;
284  ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
285      "files/click-noreferrer-links.html",
286      foo_host_port_,
287      &replacement_path));
288  NavigateToURL(shell(), test_server()->GetURL(replacement_path));
289
290  // Get the original SiteInstance for later comparison.
291  scoped_refptr<SiteInstance> orig_site_instance(
292      shell()->web_contents()->GetSiteInstance());
293  EXPECT_TRUE(orig_site_instance.get() != NULL);
294
295  // Test clicking a rel=noreferrer link.
296  bool success = false;
297  EXPECT_TRUE(ExecuteScriptAndExtractBool(
298      shell()->web_contents(),
299      "window.domAutomationController.send(clickNoRefLink());",
300      &success));
301  EXPECT_TRUE(success);
302
303  // Wait for the cross-site transition in the current tab to finish.
304  WaitForLoadStop(shell()->web_contents());
305
306  // Opens in same window.
307  EXPECT_EQ(1u, Shell::windows().size());
308  EXPECT_EQ("/files/title2.html",
309            shell()->web_contents()->GetLastCommittedURL().path());
310
311  // Should have the same SiteInstance.
312  scoped_refptr<SiteInstance> noref_site_instance(
313      shell()->web_contents()->GetSiteInstance());
314  EXPECT_EQ(orig_site_instance, noref_site_instance);
315}
316
317// Test for crbug.com/116192.  Targeted links should still work after the
318// named target window has swapped processes.
319IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
320                       AllowTargetedNavigationsAfterSwap) {
321  StartServer();
322
323  // Load a page with links that open in a new window.
324  std::string replacement_path;
325  ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
326      "files/click-noreferrer-links.html",
327      foo_host_port_,
328      &replacement_path));
329  NavigateToURL(shell(), test_server()->GetURL(replacement_path));
330
331  // Get the original SiteInstance for later comparison.
332  scoped_refptr<SiteInstance> orig_site_instance(
333      shell()->web_contents()->GetSiteInstance());
334  EXPECT_TRUE(orig_site_instance.get() != NULL);
335
336  // Test clicking a target=foo link.
337  ShellAddedObserver new_shell_observer;
338  bool success = false;
339  EXPECT_TRUE(ExecuteScriptAndExtractBool(
340      shell()->web_contents(),
341      "window.domAutomationController.send(clickSameSiteTargetedLink());",
342      &success));
343  EXPECT_TRUE(success);
344  Shell* new_shell = new_shell_observer.GetShell();
345
346  // Wait for the navigation in the new tab to finish, if it hasn't.
347  WaitForLoadStop(new_shell->web_contents());
348  EXPECT_EQ("/files/navigate_opener.html",
349            new_shell->web_contents()->GetLastCommittedURL().path());
350
351  // Should have the same SiteInstance.
352  scoped_refptr<SiteInstance> blank_site_instance(
353      new_shell->web_contents()->GetSiteInstance());
354  EXPECT_EQ(orig_site_instance, blank_site_instance);
355
356  // Now navigate the new tab to a different site.
357  GURL cross_site_url(GetCrossSiteURL("files/title1.html"));
358  NavigateToURL(new_shell, cross_site_url);
359  scoped_refptr<SiteInstance> new_site_instance(
360      new_shell->web_contents()->GetSiteInstance());
361  EXPECT_NE(orig_site_instance, new_site_instance);
362
363  // Clicking the original link in the first tab should cause us to swap back.
364  TestNavigationObserver navigation_observer(new_shell->web_contents());
365  EXPECT_TRUE(ExecuteScriptAndExtractBool(
366      shell()->web_contents(),
367      "window.domAutomationController.send(clickSameSiteTargetedLink());",
368      &success));
369  EXPECT_TRUE(success);
370  navigation_observer.Wait();
371
372  // Should have swapped back and shown the new window again.
373  scoped_refptr<SiteInstance> revisit_site_instance(
374      new_shell->web_contents()->GetSiteInstance());
375  EXPECT_EQ(orig_site_instance, revisit_site_instance);
376
377  // If it navigates away to another process, the original window should
378  // still be able to close it (using a cross-process close message).
379  NavigateToURL(new_shell, cross_site_url);
380  EXPECT_EQ(new_site_instance,
381            new_shell->web_contents()->GetSiteInstance());
382  WebContentsDestroyedWatcher close_watcher(new_shell->web_contents());
383  EXPECT_TRUE(ExecuteScriptAndExtractBool(
384      shell()->web_contents(),
385      "window.domAutomationController.send(testCloseWindow());",
386      &success));
387  EXPECT_TRUE(success);
388  close_watcher.Wait();
389}
390
391// Test that setting the opener to null in a window affects cross-process
392// navigations, including those to existing entries.  http://crbug.com/156669.
393// Flaky on windows: http://crbug.com/291249
394// This test also crashes under ThreadSanitizer, http://crbug.com/356758.
395#if defined(OS_WIN) || defined(THREAD_SANITIZER)
396#define MAYBE_DisownOpener DISABLED_DisownOpener
397#else
398#define MAYBE_DisownOpener DisownOpener
399#endif
400IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, MAYBE_DisownOpener) {
401  StartServer();
402
403  // Load a page with links that open in a new window.
404  std::string replacement_path;
405  ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
406      "files/click-noreferrer-links.html",
407      foo_host_port_,
408      &replacement_path));
409  NavigateToURL(shell(), test_server()->GetURL(replacement_path));
410
411  // Get the original SiteInstance for later comparison.
412  scoped_refptr<SiteInstance> orig_site_instance(
413      shell()->web_contents()->GetSiteInstance());
414  EXPECT_TRUE(orig_site_instance.get() != NULL);
415
416  // Test clicking a target=_blank link.
417  ShellAddedObserver new_shell_observer;
418  bool success = false;
419  EXPECT_TRUE(ExecuteScriptAndExtractBool(
420      shell()->web_contents(),
421      "window.domAutomationController.send(clickSameSiteTargetBlankLink());",
422      &success));
423  EXPECT_TRUE(success);
424  Shell* new_shell = new_shell_observer.GetShell();
425
426  // Wait for the navigation in the new tab to finish, if it hasn't.
427  WaitForLoadStop(new_shell->web_contents());
428  EXPECT_EQ("/files/title2.html",
429            new_shell->web_contents()->GetLastCommittedURL().path());
430
431  // Should have the same SiteInstance.
432  scoped_refptr<SiteInstance> blank_site_instance(
433      new_shell->web_contents()->GetSiteInstance());
434  EXPECT_EQ(orig_site_instance, blank_site_instance);
435
436  // Now navigate the new tab to a different site.
437  NavigateToURL(new_shell, GetCrossSiteURL("files/title1.html"));
438  scoped_refptr<SiteInstance> new_site_instance(
439      new_shell->web_contents()->GetSiteInstance());
440  EXPECT_NE(orig_site_instance, new_site_instance);
441
442  // Now disown the opener.
443  EXPECT_TRUE(ExecuteScript(new_shell->web_contents(),
444                            "window.opener = null;"));
445
446  // Go back and ensure the opener is still null.
447  {
448    TestNavigationObserver back_nav_load_observer(new_shell->web_contents());
449    new_shell->web_contents()->GetController().GoBack();
450    back_nav_load_observer.Wait();
451  }
452  success = false;
453  EXPECT_TRUE(ExecuteScriptAndExtractBool(
454      new_shell->web_contents(),
455      "window.domAutomationController.send(window.opener == null);",
456      &success));
457  EXPECT_TRUE(success);
458
459  // Now navigate forward again (creating a new process) and check opener.
460  NavigateToURL(new_shell, GetCrossSiteURL("files/title1.html"));
461  success = false;
462  EXPECT_TRUE(ExecuteScriptAndExtractBool(
463      new_shell->web_contents(),
464      "window.domAutomationController.send(window.opener == null);",
465      &success));
466  EXPECT_TRUE(success);
467}
468
469// Test that subframes can disown their openers.  http://crbug.com/225528.
470IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, DisownSubframeOpener) {
471  const GURL frame_url("data:text/html,<iframe name=\"foo\"></iframe>");
472  NavigateToURL(shell(), frame_url);
473
474  // Give the frame an opener using window.open.
475  EXPECT_TRUE(ExecuteScript(shell()->web_contents(),
476                            "window.open('about:blank','foo');"));
477
478  // Now disown the frame's opener.  Shouldn't crash.
479  EXPECT_TRUE(ExecuteScript(shell()->web_contents(),
480                            "window.frames[0].opener = null;"));
481}
482
483// Test for crbug.com/99202.  PostMessage calls should still work after
484// navigating the source and target windows to different sites.
485// Specifically:
486// 1) Create 3 windows (opener, "foo", and _blank) and send "foo" cross-process.
487// 2) Fail to post a message from "foo" to opener with the wrong target origin.
488// 3) Post a message from "foo" to opener, which replies back to "foo".
489// 4) Post a message from _blank to "foo".
490// 5) Post a message from "foo" to a subframe of opener, which replies back.
491// 6) Post a message from _blank to a subframe of "foo".
492IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
493                       SupportCrossProcessPostMessage) {
494  StartServer();
495
496  // Load a page with links that open in a new window.
497  std::string replacement_path;
498  ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
499      "files/click-noreferrer-links.html",
500      foo_host_port_,
501      &replacement_path));
502  NavigateToURL(shell(), test_server()->GetURL(replacement_path));
503
504  // Get the original SiteInstance and RVHM for later comparison.
505  WebContents* opener_contents = shell()->web_contents();
506  scoped_refptr<SiteInstance> orig_site_instance(
507      opener_contents->GetSiteInstance());
508  EXPECT_TRUE(orig_site_instance.get() != NULL);
509  RenderFrameHostManager* opener_manager = static_cast<WebContentsImpl*>(
510      opener_contents)->GetRenderManagerForTesting();
511
512  // 1) Open two more windows, one named.  These initially have openers but no
513  // reference to each other.  We will later post a message between them.
514
515  // First, a named target=foo window.
516  ShellAddedObserver new_shell_observer;
517  bool success = false;
518  EXPECT_TRUE(ExecuteScriptAndExtractBool(
519      opener_contents,
520      "window.domAutomationController.send(clickSameSiteTargetedLink());",
521      &success));
522  EXPECT_TRUE(success);
523  Shell* new_shell = new_shell_observer.GetShell();
524
525  // Wait for the navigation in the new window to finish, if it hasn't, then
526  // send it to post_message.html on a different site.
527  WebContents* foo_contents = new_shell->web_contents();
528  WaitForLoadStop(foo_contents);
529  EXPECT_EQ("/files/navigate_opener.html",
530            foo_contents->GetLastCommittedURL().path());
531  NavigateToURL(new_shell, GetCrossSiteURL("files/post_message.html"));
532  scoped_refptr<SiteInstance> foo_site_instance(
533      foo_contents->GetSiteInstance());
534  EXPECT_NE(orig_site_instance, foo_site_instance);
535
536  // Second, a target=_blank window.
537  ShellAddedObserver new_shell_observer2;
538  EXPECT_TRUE(ExecuteScriptAndExtractBool(
539      shell()->web_contents(),
540      "window.domAutomationController.send(clickSameSiteTargetBlankLink());",
541      &success));
542  EXPECT_TRUE(success);
543
544  // Wait for the navigation in the new window to finish, if it hasn't, then
545  // send it to post_message.html on the original site.
546  Shell* new_shell2 = new_shell_observer2.GetShell();
547  WebContents* new_contents = new_shell2->web_contents();
548  WaitForLoadStop(new_contents);
549  EXPECT_EQ("/files/title2.html", new_contents->GetLastCommittedURL().path());
550  NavigateToURL(new_shell2, test_server()->GetURL("files/post_message.html"));
551  EXPECT_EQ(orig_site_instance, new_contents->GetSiteInstance());
552  RenderFrameHostManager* new_manager =
553      static_cast<WebContentsImpl*>(new_contents)->GetRenderManagerForTesting();
554
555  // We now have three windows.  The opener should have a swapped out RVH
556  // for the new SiteInstance, but the _blank window should not.
557  EXPECT_EQ(3u, Shell::windows().size());
558  EXPECT_TRUE(
559      opener_manager->GetSwappedOutRenderViewHost(foo_site_instance.get()));
560  EXPECT_FALSE(
561      new_manager->GetSwappedOutRenderViewHost(foo_site_instance.get()));
562
563  // 2) Fail to post a message from the foo window to the opener if the target
564  // origin is wrong.  We won't see an error, but we can check for the right
565  // number of received messages below.
566  EXPECT_TRUE(ExecuteScriptAndExtractBool(
567      foo_contents,
568      "window.domAutomationController.send(postToOpener('msg',"
569      "    'http://google.com'));",
570      &success));
571  EXPECT_TRUE(success);
572  ASSERT_FALSE(
573      opener_manager->GetSwappedOutRenderViewHost(orig_site_instance.get()));
574
575  // 3) Post a message from the foo window to the opener.  The opener will
576  // reply, causing the foo window to update its own title.
577  base::string16 expected_title = ASCIIToUTF16("msg");
578  TitleWatcher title_watcher(foo_contents, expected_title);
579  EXPECT_TRUE(ExecuteScriptAndExtractBool(
580      foo_contents,
581      "window.domAutomationController.send(postToOpener('msg','*'));",
582      &success));
583  EXPECT_TRUE(success);
584  ASSERT_FALSE(
585      opener_manager->GetSwappedOutRenderViewHost(orig_site_instance.get()));
586  ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
587
588  // We should have received only 1 message in the opener and "foo" tabs,
589  // and updated the title.
590  int opener_received_messages = 0;
591  EXPECT_TRUE(ExecuteScriptAndExtractInt(
592      opener_contents,
593      "window.domAutomationController.send(window.receivedMessages);",
594      &opener_received_messages));
595  int foo_received_messages = 0;
596  EXPECT_TRUE(ExecuteScriptAndExtractInt(
597      foo_contents,
598      "window.domAutomationController.send(window.receivedMessages);",
599      &foo_received_messages));
600  EXPECT_EQ(1, foo_received_messages);
601  EXPECT_EQ(1, opener_received_messages);
602  EXPECT_EQ(ASCIIToUTF16("msg"), foo_contents->GetTitle());
603
604  // 4) Now post a message from the _blank window to the foo window.  The
605  // foo window will update its title and will not reply.
606  expected_title = ASCIIToUTF16("msg2");
607  TitleWatcher title_watcher2(foo_contents, expected_title);
608  EXPECT_TRUE(ExecuteScriptAndExtractBool(
609      new_contents,
610      "window.domAutomationController.send(postToFoo('msg2'));",
611      &success));
612  EXPECT_TRUE(success);
613  ASSERT_EQ(expected_title, title_watcher2.WaitAndGetTitle());
614
615  // This postMessage should have created a swapped out RVH for the new
616  // SiteInstance in the target=_blank window.
617  EXPECT_TRUE(
618      new_manager->GetSwappedOutRenderViewHost(foo_site_instance.get()));
619
620  // TODO(nasko): Test subframe targeting of postMessage once
621  // http://crbug.com/153701 is fixed.
622}
623
624// Test for crbug.com/278336. MessagePorts should work cross-process. I.e.,
625// messages which contain Transferables and get intercepted by
626// RenderViewImpl::willCheckAndDispatchMessageEvent (because the RenderView is
627// swapped out) should work.
628// Specifically:
629// 1) Create 2 windows (opener and "foo") and send "foo" cross-process.
630// 2) Post a message containing a message port from opener to "foo".
631// 3) Post a message from "foo" back to opener via the passed message port.
632// The test will be enabled when the feature implementation lands.
633IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
634                       SupportCrossProcessPostMessageWithMessagePort) {
635  StartServer();
636
637  // Load a page with links that open in a new window.
638  std::string replacement_path;
639  ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
640      "files/click-noreferrer-links.html",
641      foo_host_port_,
642      &replacement_path));
643  NavigateToURL(shell(), test_server()->GetURL(replacement_path));
644
645  // Get the original SiteInstance and RVHM for later comparison.
646  WebContents* opener_contents = shell()->web_contents();
647  scoped_refptr<SiteInstance> orig_site_instance(
648      opener_contents->GetSiteInstance());
649  EXPECT_TRUE(orig_site_instance.get() != NULL);
650  RenderFrameHostManager* opener_manager = static_cast<WebContentsImpl*>(
651      opener_contents)->GetRenderManagerForTesting();
652
653  // 1) Open a named target=foo window. We will later post a message between the
654  // opener and the new window.
655  ShellAddedObserver new_shell_observer;
656  bool success = false;
657  EXPECT_TRUE(ExecuteScriptAndExtractBool(
658      opener_contents,
659      "window.domAutomationController.send(clickSameSiteTargetedLink());",
660      &success));
661  EXPECT_TRUE(success);
662  Shell* new_shell = new_shell_observer.GetShell();
663
664  // Wait for the navigation in the new window to finish, if it hasn't, then
665  // send it to post_message.html on a different site.
666  WebContents* foo_contents = new_shell->web_contents();
667  WaitForLoadStop(foo_contents);
668  EXPECT_EQ("/files/navigate_opener.html",
669            foo_contents->GetLastCommittedURL().path());
670  NavigateToURL(new_shell, GetCrossSiteURL("files/post_message.html"));
671  scoped_refptr<SiteInstance> foo_site_instance(
672      foo_contents->GetSiteInstance());
673  EXPECT_NE(orig_site_instance, foo_site_instance);
674
675  // We now have two windows. The opener should have a swapped out RVH
676  // for the new SiteInstance.
677  EXPECT_EQ(2u, Shell::windows().size());
678  EXPECT_TRUE(
679      opener_manager->GetSwappedOutRenderViewHost(foo_site_instance.get()));
680
681  // 2) Post a message containing a MessagePort from opener to the the foo
682  // window. The foo window will reply via the passed port, causing the opener
683  // to update its own title.
684  base::string16 expected_title = ASCIIToUTF16("msg-back-via-port");
685  TitleWatcher title_observer(opener_contents, expected_title);
686  EXPECT_TRUE(ExecuteScriptAndExtractBool(
687      opener_contents,
688      "window.domAutomationController.send(postWithPortToFoo());",
689      &success));
690  EXPECT_TRUE(success);
691  ASSERT_FALSE(
692      opener_manager->GetSwappedOutRenderViewHost(orig_site_instance.get()));
693  ASSERT_EQ(expected_title, title_observer.WaitAndGetTitle());
694
695  // Check message counts.
696  int opener_received_messages_via_port = 0;
697  EXPECT_TRUE(ExecuteScriptAndExtractInt(
698      opener_contents,
699      "window.domAutomationController.send(window.receivedMessagesViaPort);",
700      &opener_received_messages_via_port));
701  int foo_received_messages = 0;
702  EXPECT_TRUE(ExecuteScriptAndExtractInt(
703      foo_contents,
704      "window.domAutomationController.send(window.receivedMessages);",
705      &foo_received_messages));
706  int foo_received_messages_with_port = 0;
707  EXPECT_TRUE(ExecuteScriptAndExtractInt(
708      foo_contents,
709      "window.domAutomationController.send(window.receivedMessagesWithPort);",
710      &foo_received_messages_with_port));
711  EXPECT_EQ(1, foo_received_messages);
712  EXPECT_EQ(1, foo_received_messages_with_port);
713  EXPECT_EQ(1, opener_received_messages_via_port);
714  EXPECT_EQ(ASCIIToUTF16("msg-with-port"), foo_contents->GetTitle());
715  EXPECT_EQ(ASCIIToUTF16("msg-back-via-port"), opener_contents->GetTitle());
716}
717
718// Test for crbug.com/116192.  Navigations to a window's opener should
719// still work after a process swap.
720IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
721                       AllowTargetedNavigationsInOpenerAfterSwap) {
722  StartServer();
723
724  // Load a page with links that open in a new window.
725  std::string replacement_path;
726  ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
727      "files/click-noreferrer-links.html",
728      foo_host_port_,
729      &replacement_path));
730  NavigateToURL(shell(), test_server()->GetURL(replacement_path));
731
732  // Get the original tab and SiteInstance for later comparison.
733  WebContents* orig_contents = shell()->web_contents();
734  scoped_refptr<SiteInstance> orig_site_instance(
735      orig_contents->GetSiteInstance());
736  EXPECT_TRUE(orig_site_instance.get() != NULL);
737
738  // Test clicking a target=foo link.
739  ShellAddedObserver new_shell_observer;
740  bool success = false;
741  EXPECT_TRUE(ExecuteScriptAndExtractBool(
742      orig_contents,
743      "window.domAutomationController.send(clickSameSiteTargetedLink());",
744      &success));
745  EXPECT_TRUE(success);
746  Shell* new_shell = new_shell_observer.GetShell();
747
748  // Wait for the navigation in the new window to finish, if it hasn't.
749  WaitForLoadStop(new_shell->web_contents());
750  EXPECT_EQ("/files/navigate_opener.html",
751            new_shell->web_contents()->GetLastCommittedURL().path());
752
753  // Should have the same SiteInstance.
754  scoped_refptr<SiteInstance> blank_site_instance(
755      new_shell->web_contents()->GetSiteInstance());
756  EXPECT_EQ(orig_site_instance, blank_site_instance);
757
758  // Now navigate the original (opener) tab to a different site.
759  NavigateToURL(shell(), GetCrossSiteURL("files/title1.html"));
760  scoped_refptr<SiteInstance> new_site_instance(
761      shell()->web_contents()->GetSiteInstance());
762  EXPECT_NE(orig_site_instance, new_site_instance);
763
764  // The opened tab should be able to navigate the opener back to its process.
765  TestNavigationObserver navigation_observer(orig_contents);
766  EXPECT_TRUE(ExecuteScriptAndExtractBool(
767      new_shell->web_contents(),
768      "window.domAutomationController.send(navigateOpener());",
769      &success));
770  EXPECT_TRUE(success);
771  navigation_observer.Wait();
772
773  // Should have swapped back into this process.
774  scoped_refptr<SiteInstance> revisit_site_instance(
775      shell()->web_contents()->GetSiteInstance());
776  EXPECT_EQ(orig_site_instance, revisit_site_instance);
777}
778
779// Test that opening a new window in the same SiteInstance and then navigating
780// both windows to a different SiteInstance allows the first process to exit.
781// See http://crbug.com/126333.
782IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
783                       ProcessExitWithSwappedOutViews) {
784  StartServer();
785
786  // Load a page with links that open in a new window.
787  std::string replacement_path;
788  ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
789      "files/click-noreferrer-links.html",
790      foo_host_port_,
791      &replacement_path));
792  NavigateToURL(shell(), test_server()->GetURL(replacement_path));
793
794  // Get the original SiteInstance for later comparison.
795  scoped_refptr<SiteInstance> orig_site_instance(
796      shell()->web_contents()->GetSiteInstance());
797  EXPECT_TRUE(orig_site_instance.get() != NULL);
798
799  // Test clicking a target=foo link.
800  ShellAddedObserver new_shell_observer;
801  bool success = false;
802  EXPECT_TRUE(ExecuteScriptAndExtractBool(
803      shell()->web_contents(),
804      "window.domAutomationController.send(clickSameSiteTargetedLink());",
805      &success));
806  EXPECT_TRUE(success);
807  Shell* new_shell = new_shell_observer.GetShell();
808
809  // Wait for the navigation in the new window to finish, if it hasn't.
810  WaitForLoadStop(new_shell->web_contents());
811  EXPECT_EQ("/files/navigate_opener.html",
812            new_shell->web_contents()->GetLastCommittedURL().path());
813
814  // Should have the same SiteInstance.
815  scoped_refptr<SiteInstance> opened_site_instance(
816      new_shell->web_contents()->GetSiteInstance());
817  EXPECT_EQ(orig_site_instance, opened_site_instance);
818
819  // Now navigate the opened window to a different site.
820  NavigateToURL(new_shell, GetCrossSiteURL("files/title1.html"));
821  scoped_refptr<SiteInstance> new_site_instance(
822      new_shell->web_contents()->GetSiteInstance());
823  EXPECT_NE(orig_site_instance, new_site_instance);
824
825  // The original process should still be alive, since it is still used in the
826  // first window.
827  RenderProcessHost* orig_process = orig_site_instance->GetProcess();
828  EXPECT_TRUE(orig_process->HasConnection());
829
830  // Navigate the first window to a different site as well.  The original
831  // process should exit, since all of its views are now swapped out.
832  RenderProcessHostWatcher exit_observer(
833      orig_process,
834      RenderProcessHostWatcher::WATCH_FOR_HOST_DESTRUCTION);
835  NavigateToURL(shell(), GetCrossSiteURL("files/title1.html"));
836  exit_observer.Wait();
837  scoped_refptr<SiteInstance> new_site_instance2(
838      shell()->web_contents()->GetSiteInstance());
839  EXPECT_EQ(new_site_instance, new_site_instance2);
840}
841
842// Test for crbug.com/76666.  A cross-site navigation that fails with a 204
843// error should not make us ignore future renderer-initiated navigations.
844IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, ClickLinkAfter204Error) {
845  StartServer();
846
847  // Load a page with links that open in a new window.
848  // The links will point to foo.com.
849  std::string replacement_path;
850  ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
851      "files/click-noreferrer-links.html",
852      foo_host_port_,
853      &replacement_path));
854  NavigateToURL(shell(), test_server()->GetURL(replacement_path));
855
856  // Get the original SiteInstance for later comparison.
857  scoped_refptr<SiteInstance> orig_site_instance(
858      shell()->web_contents()->GetSiteInstance());
859  EXPECT_TRUE(orig_site_instance.get() != NULL);
860
861  // Load a cross-site page that fails with a 204 error.
862  NavigateToURL(shell(),GetCrossSiteURL("nocontent"));
863
864  // We should still be looking at the normal page.  The typed URL will
865  // still be visible until the user clears it manually, but the last
866  // committed URL will be the previous page.
867  scoped_refptr<SiteInstance> post_nav_site_instance(
868      shell()->web_contents()->GetSiteInstance());
869  EXPECT_EQ(orig_site_instance, post_nav_site_instance);
870  EXPECT_EQ("/nocontent",
871            shell()->web_contents()->GetVisibleURL().path());
872  EXPECT_EQ("/files/click-noreferrer-links.html",
873            shell()->web_contents()->GetController().
874                GetLastCommittedEntry()->GetVirtualURL().path());
875
876  // Renderer-initiated navigations should work.
877  bool success = false;
878  EXPECT_TRUE(ExecuteScriptAndExtractBool(
879      shell()->web_contents(),
880      "window.domAutomationController.send(clickNoRefLink());",
881      &success));
882  EXPECT_TRUE(success);
883
884  // Wait for the cross-site transition in the current tab to finish.
885  WaitForLoadStop(shell()->web_contents());
886
887  // Opens in same tab.
888  EXPECT_EQ(1u, Shell::windows().size());
889  EXPECT_EQ("/files/title2.html",
890            shell()->web_contents()->GetLastCommittedURL().path());
891
892  // Should have the same SiteInstance.
893  scoped_refptr<SiteInstance> noref_site_instance(
894      shell()->web_contents()->GetSiteInstance());
895  EXPECT_EQ(orig_site_instance, noref_site_instance);
896}
897
898// Test for crbug.com/9682.  We should show the URL for a pending renderer-
899// initiated navigation in a new tab, until the content of the initial
900// about:blank page is modified by another window.  At that point, we should
901// revert to showing about:blank to prevent a URL spoof.
902IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, ShowLoadingURLUntilSpoof) {
903  ASSERT_TRUE(test_server()->Start());
904
905  // Load a page that can open a URL that won't commit in a new window.
906  NavigateToURL(
907      shell(), test_server()->GetURL("files/click-nocontent-link.html"));
908  WebContents* orig_contents = shell()->web_contents();
909
910  // Click a /nocontent link that opens in a new window but never commits.
911  ShellAddedObserver new_shell_observer;
912  bool success = false;
913  EXPECT_TRUE(ExecuteScriptAndExtractBool(
914      orig_contents,
915      "window.domAutomationController.send(clickNoContentTargetedLink());",
916      &success));
917  EXPECT_TRUE(success);
918
919  // Wait for the window to open.
920  Shell* new_shell = new_shell_observer.GetShell();
921
922  // Ensure the destination URL is visible, because it is considered the
923  // initial navigation.
924  WebContents* contents = new_shell->web_contents();
925  EXPECT_TRUE(contents->GetController().IsInitialNavigation());
926  EXPECT_EQ("/nocontent",
927            contents->GetController().GetVisibleEntry()->GetURL().path());
928
929  // Now modify the contents of the new window from the opener.  This will also
930  // modify the title of the document to give us something to listen for.
931  base::string16 expected_title = ASCIIToUTF16("Modified Title");
932  TitleWatcher title_watcher(contents, expected_title);
933  success = false;
934  EXPECT_TRUE(ExecuteScriptAndExtractBool(
935      orig_contents,
936      "window.domAutomationController.send(modifyNewWindow());",
937      &success));
938  EXPECT_TRUE(success);
939  ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
940
941  // At this point, we should no longer be showing the destination URL.
942  // The visible entry should be null, resulting in about:blank in the address
943  // bar.
944  EXPECT_FALSE(contents->GetController().GetVisibleEntry());
945}
946
947// Test for crbug.com/9682.  We should not show the URL for a pending renderer-
948// initiated navigation in a new tab if it is not the initial navigation.  In
949// this case, the renderer will not notify us of a modification, so we cannot
950// show the pending URL without allowing a spoof.
951IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
952                       DontShowLoadingURLIfNotInitialNav) {
953  ASSERT_TRUE(test_server()->Start());
954
955  // Load a page that can open a URL that won't commit in a new window.
956  NavigateToURL(
957      shell(), test_server()->GetURL("files/click-nocontent-link.html"));
958  WebContents* orig_contents = shell()->web_contents();
959
960  // Click a /nocontent link that opens in a new window but never commits.
961  // By using an onclick handler that first creates the window, the slow
962  // navigation is not considered an initial navigation.
963  ShellAddedObserver new_shell_observer;
964  bool success = false;
965  EXPECT_TRUE(ExecuteScriptAndExtractBool(
966      orig_contents,
967      "window.domAutomationController.send("
968      "clickNoContentScriptedTargetedLink());",
969      &success));
970  EXPECT_TRUE(success);
971
972  // Wait for the window to open.
973  Shell* new_shell = new_shell_observer.GetShell();
974
975  // Ensure the destination URL is not visible, because it is not the initial
976  // navigation.
977  WebContents* contents = new_shell->web_contents();
978  EXPECT_FALSE(contents->GetController().IsInitialNavigation());
979  EXPECT_FALSE(contents->GetController().GetVisibleEntry());
980}
981
982// Crashes under ThreadSanitizer, http://crbug.com/356758.
983#if defined(THREAD_SANITIZER)
984#define MAYBE_BackForwardNotStale DISABLED_BackForwardNotStale
985#else
986#define MAYBE_BackForwardNotStale BackForwardNotStale
987#endif
988// Test for http://crbug.com/93427.  Ensure that cross-site navigations
989// do not cause back/forward navigations to be considered stale by the
990// renderer.
991IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, MAYBE_BackForwardNotStale) {
992  StartServer();
993  NavigateToURL(shell(), GURL(kAboutBlankURL));
994
995  // Visit a page on first site.
996  NavigateToURL(shell(), test_server()->GetURL("files/title1.html"));
997
998  // Visit three pages on second site.
999  NavigateToURL(shell(), GetCrossSiteURL("files/title1.html"));
1000  NavigateToURL(shell(), GetCrossSiteURL("files/title2.html"));
1001  NavigateToURL(shell(), GetCrossSiteURL("files/title3.html"));
1002
1003  // History is now [blank, A1, B1, B2, *B3].
1004  WebContents* contents = shell()->web_contents();
1005  EXPECT_EQ(5, contents->GetController().GetEntryCount());
1006
1007  // Open another window in same process to keep this process alive.
1008  Shell* new_shell = CreateBrowser();
1009  NavigateToURL(new_shell, GetCrossSiteURL("files/title1.html"));
1010
1011  // Go back three times to first site.
1012  {
1013    TestNavigationObserver back_nav_load_observer(shell()->web_contents());
1014    shell()->web_contents()->GetController().GoBack();
1015    back_nav_load_observer.Wait();
1016  }
1017  {
1018    TestNavigationObserver back_nav_load_observer(shell()->web_contents());
1019    shell()->web_contents()->GetController().GoBack();
1020    back_nav_load_observer.Wait();
1021  }
1022  {
1023    TestNavigationObserver back_nav_load_observer(shell()->web_contents());
1024    shell()->web_contents()->GetController().GoBack();
1025    back_nav_load_observer.Wait();
1026  }
1027
1028  // Now go forward twice to B2.  Shouldn't be left spinning.
1029  {
1030    TestNavigationObserver forward_nav_load_observer(shell()->web_contents());
1031    shell()->web_contents()->GetController().GoForward();
1032    forward_nav_load_observer.Wait();
1033  }
1034  {
1035    TestNavigationObserver forward_nav_load_observer(shell()->web_contents());
1036    shell()->web_contents()->GetController().GoForward();
1037    forward_nav_load_observer.Wait();
1038  }
1039
1040  // Go back twice to first site.
1041  {
1042    TestNavigationObserver back_nav_load_observer(shell()->web_contents());
1043    shell()->web_contents()->GetController().GoBack();
1044    back_nav_load_observer.Wait();
1045  }
1046  {
1047    TestNavigationObserver back_nav_load_observer(shell()->web_contents());
1048    shell()->web_contents()->GetController().GoBack();
1049    back_nav_load_observer.Wait();
1050  }
1051
1052  // Now go forward directly to B3.  Shouldn't be left spinning.
1053  {
1054    TestNavigationObserver forward_nav_load_observer(shell()->web_contents());
1055    shell()->web_contents()->GetController().GoToIndex(4);
1056    forward_nav_load_observer.Wait();
1057  }
1058}
1059
1060// Test for http://crbug.com/130016.
1061// Swapping out a render view should update its visiblity state.
1062IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
1063                       SwappedOutViewHasCorrectVisibilityState) {
1064  StartServer();
1065
1066  // Load a page with links that open in a new window.
1067  std::string replacement_path;
1068  ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
1069      "files/click-noreferrer-links.html",
1070      foo_host_port_,
1071      &replacement_path));
1072  NavigateToURL(shell(), test_server()->GetURL(replacement_path));
1073
1074  // Open a same-site link in a new widnow.
1075  ShellAddedObserver new_shell_observer;
1076  bool success = false;
1077  EXPECT_TRUE(ExecuteScriptAndExtractBool(
1078      shell()->web_contents(),
1079      "window.domAutomationController.send(clickSameSiteTargetedLink());",
1080      &success));
1081  EXPECT_TRUE(success);
1082  Shell* new_shell = new_shell_observer.GetShell();
1083
1084  // Wait for the navigation in the new tab to finish, if it hasn't.
1085  WaitForLoadStop(new_shell->web_contents());
1086  EXPECT_EQ("/files/navigate_opener.html",
1087            new_shell->web_contents()->GetLastCommittedURL().path());
1088
1089  RenderViewHost* rvh = new_shell->web_contents()->GetRenderViewHost();
1090
1091  EXPECT_TRUE(ExecuteScriptAndExtractBool(
1092      rvh,
1093      "window.domAutomationController.send("
1094      "    document.visibilityState == 'visible');",
1095      &success));
1096  EXPECT_TRUE(success);
1097
1098  // Now navigate the new window to a different site. This should swap out the
1099  // tab's existing RenderView, causing it become hidden.
1100  NavigateToURL(new_shell, GetCrossSiteURL("files/title1.html"));
1101
1102  EXPECT_TRUE(ExecuteScriptAndExtractBool(
1103      rvh,
1104      "window.domAutomationController.send("
1105      "    document.visibilityState == 'hidden');",
1106      &success));
1107  EXPECT_TRUE(success);
1108
1109  // Going back should make the previously swapped-out view to become visible
1110  // again.
1111  {
1112    TestNavigationObserver back_nav_load_observer(new_shell->web_contents());
1113    new_shell->web_contents()->GetController().GoBack();
1114    back_nav_load_observer.Wait();
1115  }
1116
1117  EXPECT_EQ("/files/navigate_opener.html",
1118            new_shell->web_contents()->GetLastCommittedURL().path());
1119
1120  EXPECT_EQ(rvh, new_shell->web_contents()->GetRenderViewHost());
1121
1122  EXPECT_TRUE(ExecuteScriptAndExtractBool(
1123      rvh,
1124      "window.domAutomationController.send("
1125      "    document.visibilityState == 'visible');",
1126      &success));
1127  EXPECT_TRUE(success);
1128}
1129
1130// This class ensures that all the given RenderViewHosts have properly been
1131// shutdown.
1132class RenderViewHostDestructionObserver : public WebContentsObserver {
1133 public:
1134  explicit RenderViewHostDestructionObserver(WebContents* web_contents)
1135      : WebContentsObserver(web_contents) {}
1136  virtual ~RenderViewHostDestructionObserver() {}
1137  void EnsureRVHGetsDestructed(RenderViewHost* rvh) {
1138    watched_render_view_hosts_.insert(rvh);
1139  }
1140  size_t GetNumberOfWatchedRenderViewHosts() const {
1141    return watched_render_view_hosts_.size();
1142  }
1143
1144 private:
1145  // WebContentsObserver implementation:
1146  virtual void RenderViewDeleted(RenderViewHost* rvh) OVERRIDE {
1147    watched_render_view_hosts_.erase(rvh);
1148  }
1149
1150  std::set<RenderViewHost*> watched_render_view_hosts_;
1151};
1152
1153// Crashes under ThreadSanitizer, http://crbug.com/356758.
1154#if defined(THREAD_SANITIZER)
1155#define MAYBE_LeakingRenderViewHosts DISABLED_LeakingRenderViewHosts
1156#else
1157#define MAYBE_LeakingRenderViewHosts LeakingRenderViewHosts
1158#endif
1159// Test for crbug.com/90867. Make sure we don't leak render view hosts since
1160// they may cause crashes or memory corruptions when trying to call dead
1161// delegate_. This test also verifies crbug.com/117420 and crbug.com/143255 to
1162// ensure that a separate SiteInstance is created when navigating to view-source
1163// URLs, regardless of current URL.
1164IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
1165                       MAYBE_LeakingRenderViewHosts) {
1166  StartServer();
1167
1168  // Observe the created render_view_host's to make sure they will not leak.
1169  RenderViewHostDestructionObserver rvh_observers(shell()->web_contents());
1170
1171  GURL navigated_url(test_server()->GetURL("files/title2.html"));
1172  GURL view_source_url(kViewSourceScheme + std::string(":") +
1173                       navigated_url.spec());
1174
1175  // Let's ensure that when we start with a blank window, navigating away to a
1176  // view-source URL, we create a new SiteInstance.
1177  RenderViewHost* blank_rvh = shell()->web_contents()->GetRenderViewHost();
1178  SiteInstance* blank_site_instance = blank_rvh->GetSiteInstance();
1179  EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), GURL::EmptyGURL());
1180  EXPECT_EQ(blank_site_instance->GetSiteURL(), GURL::EmptyGURL());
1181  rvh_observers.EnsureRVHGetsDestructed(blank_rvh);
1182
1183  // Now navigate to the view-source URL and ensure we got a different
1184  // SiteInstance and RenderViewHost.
1185  NavigateToURL(shell(), view_source_url);
1186  EXPECT_NE(blank_rvh, shell()->web_contents()->GetRenderViewHost());
1187  EXPECT_NE(blank_site_instance, shell()->web_contents()->
1188      GetRenderViewHost()->GetSiteInstance());
1189  rvh_observers.EnsureRVHGetsDestructed(
1190      shell()->web_contents()->GetRenderViewHost());
1191
1192  // Load a random page and then navigate to view-source: of it.
1193  // This used to cause two RVH instances for the same SiteInstance, which
1194  // was a problem.  This is no longer the case.
1195  NavigateToURL(shell(), navigated_url);
1196  SiteInstance* site_instance1 = shell()->web_contents()->
1197      GetRenderViewHost()->GetSiteInstance();
1198  rvh_observers.EnsureRVHGetsDestructed(
1199      shell()->web_contents()->GetRenderViewHost());
1200
1201  NavigateToURL(shell(), view_source_url);
1202  rvh_observers.EnsureRVHGetsDestructed(
1203      shell()->web_contents()->GetRenderViewHost());
1204  SiteInstance* site_instance2 = shell()->web_contents()->
1205      GetRenderViewHost()->GetSiteInstance();
1206
1207  // Ensure that view-source navigations force a new SiteInstance.
1208  EXPECT_NE(site_instance1, site_instance2);
1209
1210  // Now navigate to a different instance so that we swap out again.
1211  NavigateToURL(shell(), GetCrossSiteURL("files/title2.html"));
1212  rvh_observers.EnsureRVHGetsDestructed(
1213      shell()->web_contents()->GetRenderViewHost());
1214
1215  // This used to leak a render view host.
1216  shell()->Close();
1217
1218  RunAllPendingInMessageLoop();  // Needed on ChromeOS.
1219
1220  EXPECT_EQ(0U, rvh_observers.GetNumberOfWatchedRenderViewHosts());
1221}
1222
1223// Test for crbug.com/143155.  Frame tree updates during unload should not
1224// interrupt the intended navigation and show swappedout:// instead.
1225// Specifically:
1226// 1) Open 2 tabs in an HTTP SiteInstance, with a subframe in the opener.
1227// 2) Send the second tab to a different foo.com SiteInstance.
1228//    This creates a swapped out opener for the first tab in the foo process.
1229// 3) Navigate the first tab to the foo.com SiteInstance, and have the first
1230//    tab's unload handler remove its frame.
1231// This used to cause an update to the frame tree of the swapped out RV,
1232// just as it was navigating to a real page.  That pre-empted the real
1233// navigation and visibly sent the tab to swappedout://.
1234IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
1235                       DontPreemptNavigationWithFrameTreeUpdate) {
1236  StartServer();
1237
1238  // 1. Load a page that deletes its iframe during unload.
1239  NavigateToURL(shell(),
1240                test_server()->GetURL("files/remove_frame_on_unload.html"));
1241
1242  // Get the original SiteInstance for later comparison.
1243  scoped_refptr<SiteInstance> orig_site_instance(
1244      shell()->web_contents()->GetSiteInstance());
1245
1246  // Open a same-site page in a new window.
1247  ShellAddedObserver new_shell_observer;
1248  bool success = false;
1249  EXPECT_TRUE(ExecuteScriptAndExtractBool(
1250      shell()->web_contents(),
1251      "window.domAutomationController.send(openWindow());",
1252      &success));
1253  EXPECT_TRUE(success);
1254  Shell* new_shell = new_shell_observer.GetShell();
1255
1256  // Wait for the navigation in the new window to finish, if it hasn't.
1257  WaitForLoadStop(new_shell->web_contents());
1258  EXPECT_EQ("/files/title1.html",
1259            new_shell->web_contents()->GetLastCommittedURL().path());
1260
1261  // Should have the same SiteInstance.
1262  EXPECT_EQ(orig_site_instance, new_shell->web_contents()->GetSiteInstance());
1263
1264  // 2. Send the second tab to a different process.
1265  NavigateToURL(new_shell, GetCrossSiteURL("files/title1.html"));
1266  scoped_refptr<SiteInstance> new_site_instance(
1267      new_shell->web_contents()->GetSiteInstance());
1268  EXPECT_NE(orig_site_instance, new_site_instance);
1269
1270  // 3. Send the first tab to the second tab's process.
1271  NavigateToURL(shell(), GetCrossSiteURL("files/title1.html"));
1272
1273  // Make sure it ends up at the right page.
1274  WaitForLoadStop(shell()->web_contents());
1275  EXPECT_EQ(GetCrossSiteURL("files/title1.html"),
1276            shell()->web_contents()->GetLastCommittedURL());
1277  EXPECT_EQ(new_site_instance, shell()->web_contents()->GetSiteInstance());
1278}
1279
1280// Ensure that renderer-side debug URLs do not cause a process swap, since they
1281// are meant to run in the current page.  We had a bug where we expected a
1282// BrowsingInstance swap to occur on pages like view-source and extensions,
1283// which broke chrome://crash and javascript: URLs.
1284// See http://crbug.com/335503.
1285IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, RendererDebugURLsDontSwap) {
1286  ASSERT_TRUE(test_server()->Start());
1287
1288  GURL original_url(test_server()->GetURL("files/title2.html"));
1289  GURL view_source_url(kViewSourceScheme + std::string(":") +
1290                       original_url.spec());
1291
1292  NavigateToURL(shell(), view_source_url);
1293
1294  // Check that javascript: URLs work.
1295  base::string16 expected_title = ASCIIToUTF16("msg");
1296  TitleWatcher title_watcher(shell()->web_contents(), expected_title);
1297  shell()->LoadURL(GURL("javascript:document.title='msg'"));
1298  ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
1299
1300  // Crash the renderer of the view-source page.
1301  RenderProcessHostWatcher crash_observer(
1302      shell()->web_contents(),
1303      RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
1304  NavigateToURL(shell(), GURL(kChromeUICrashURL));
1305  crash_observer.Wait();
1306}
1307
1308// Ensure that renderer-side debug URLs don't take effect on crashed renderers.
1309// Otherwise, we might try to load an unprivileged about:blank page into a
1310// WebUI-enabled RenderProcessHost, failing a safety check in InitRenderView.
1311// See http://crbug.com/334214.
1312IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
1313                       IgnoreRendererDebugURLsWhenCrashed) {
1314  // Visit a WebUI page with bindings.
1315  GURL webui_url = GURL(std::string(kChromeUIScheme) + "://" +
1316                        std::string(kChromeUIGpuHost));
1317  NavigateToURL(shell(), webui_url);
1318  EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
1319                  shell()->web_contents()->GetRenderProcessHost()->GetID()));
1320
1321  // Crash the renderer of the WebUI page.
1322  RenderProcessHostWatcher crash_observer(
1323      shell()->web_contents(),
1324      RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
1325  NavigateToURL(shell(), GURL(kChromeUICrashURL));
1326  crash_observer.Wait();
1327
1328  // Load the crash URL again but don't wait for any action.  If it is not
1329  // ignored this time, we will fail the WebUI CHECK in InitRenderView.
1330  shell()->LoadURL(GURL(kChromeUICrashURL));
1331
1332  // Ensure that such URLs can still work as the initial navigation of a tab.
1333  // We postpone the initial navigation of the tab using an empty GURL, so that
1334  // we can add a watcher for crashes.
1335  Shell* shell2 = Shell::CreateNewWindow(
1336      shell()->web_contents()->GetBrowserContext(), GURL(), NULL,
1337      MSG_ROUTING_NONE, gfx::Size());
1338  RenderProcessHostWatcher crash_observer2(
1339      shell2->web_contents(),
1340      RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
1341  NavigateToURL(shell2, GURL(kChromeUIKillURL));
1342  crash_observer2.Wait();
1343}
1344
1345// Ensure that pending_and_current_web_ui_ is cleared when a URL commits.
1346// Otherwise it might get picked up by InitRenderView when granting bindings
1347// to other RenderViewHosts.  See http://crbug.com/330811.
1348IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, ClearPendingWebUIOnCommit) {
1349  // Visit a WebUI page with bindings.
1350  GURL webui_url(GURL(std::string(kChromeUIScheme) + "://" +
1351                      std::string(kChromeUIGpuHost)));
1352  NavigateToURL(shell(), webui_url);
1353  EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
1354                  shell()->web_contents()->GetRenderProcessHost()->GetID()));
1355  WebContentsImpl* web_contents = static_cast<WebContentsImpl*>(
1356      shell()->web_contents());
1357  WebUIImpl* webui = web_contents->GetRenderManagerForTesting()->web_ui();
1358  EXPECT_TRUE(webui);
1359  EXPECT_FALSE(web_contents->GetRenderManagerForTesting()->pending_web_ui());
1360
1361  // Navigate to another WebUI URL that reuses the WebUI object.  Make sure we
1362  // clear pending_web_ui() when it commits.
1363  GURL webui_url2(webui_url.spec() + "#foo");
1364  NavigateToURL(shell(), webui_url2);
1365  EXPECT_EQ(webui, web_contents->GetRenderManagerForTesting()->web_ui());
1366  EXPECT_FALSE(web_contents->GetRenderManagerForTesting()->pending_web_ui());
1367}
1368
1369class RFHMProcessPerTabTest : public RenderFrameHostManagerTest {
1370 public:
1371  RFHMProcessPerTabTest() {}
1372
1373  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
1374    command_line->AppendSwitch(switches::kProcessPerTab);
1375  }
1376};
1377
1378// Test that we still swap processes for BrowsingInstance changes even in
1379// --process-per-tab mode.  See http://crbug.com/343017.
1380// Disabled on Android: http://crbug.com/345873.
1381// Crashes under ThreadSanitizer, http://crbug.com/356758.
1382#if defined(OS_ANDROID) || defined(THREAD_SANITIZER)
1383#define MAYBE_BackFromWebUI DISABLED_BackFromWebUI
1384#else
1385#define MAYBE_BackFromWebUI BackFromWebUI
1386#endif
1387IN_PROC_BROWSER_TEST_F(RFHMProcessPerTabTest, MAYBE_BackFromWebUI) {
1388  ASSERT_TRUE(test_server()->Start());
1389  GURL original_url(test_server()->GetURL("files/title2.html"));
1390  NavigateToURL(shell(), original_url);
1391
1392  // Visit a WebUI page with bindings.
1393  GURL webui_url(GURL(std::string(kChromeUIScheme) + "://" +
1394                      std::string(kChromeUIGpuHost)));
1395  NavigateToURL(shell(), webui_url);
1396  EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
1397                  shell()->web_contents()->GetRenderProcessHost()->GetID()));
1398
1399  // Go back and ensure we have no WebUI bindings.
1400  TestNavigationObserver back_nav_load_observer(shell()->web_contents());
1401  shell()->web_contents()->GetController().GoBack();
1402  back_nav_load_observer.Wait();
1403  EXPECT_EQ(original_url, shell()->web_contents()->GetLastCommittedURL());
1404  EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
1405                  shell()->web_contents()->GetRenderProcessHost()->GetID()));
1406}
1407
1408}  // namespace content
1409