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