render_frame_host_manager_browsertest.cc revision 03b57e008b61dfcb1fbad3aea950ae0e001748b0
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// This test crashes under ThreadSanitizer, http://crbug.com/356758.
415#if defined(THREAD_SANITIZER)
416#define MAYBE_DisownOpener DISABLED_DisownOpener
417#else
418#define MAYBE_DisownOpener DisownOpener
419#endif
420IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, MAYBE_DisownOpener) {
421  StartServer();
422
423  // Load a page with links that open in a new window.
424  std::string replacement_path;
425  ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
426      "files/click-noreferrer-links.html",
427      foo_host_port_,
428      &replacement_path));
429  NavigateToURL(shell(), test_server()->GetURL(replacement_path));
430
431  // Get the original SiteInstance for later comparison.
432  scoped_refptr<SiteInstance> orig_site_instance(
433      shell()->web_contents()->GetSiteInstance());
434  EXPECT_TRUE(orig_site_instance.get() != NULL);
435
436  // Test clicking a target=_blank link.
437  ShellAddedObserver new_shell_observer;
438  bool success = false;
439  EXPECT_TRUE(ExecuteScriptAndExtractBool(
440      shell()->web_contents(),
441      "window.domAutomationController.send(clickSameSiteTargetBlankLink());",
442      &success));
443  EXPECT_TRUE(success);
444  Shell* new_shell = new_shell_observer.GetShell();
445  EXPECT_TRUE(new_shell->web_contents()->HasOpener());
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  EXPECT_TRUE(new_shell->web_contents()->HasOpener());
463
464  // Now disown the opener.
465  EXPECT_TRUE(ExecuteScript(new_shell->web_contents(),
466                            "window.opener = null;"));
467  EXPECT_FALSE(new_shell->web_contents()->HasOpener());
468
469  // Go back and ensure the opener is still null.
470  {
471    TestNavigationObserver back_nav_load_observer(new_shell->web_contents());
472    new_shell->web_contents()->GetController().GoBack();
473    back_nav_load_observer.Wait();
474  }
475  success = false;
476  EXPECT_TRUE(ExecuteScriptAndExtractBool(
477      new_shell->web_contents(),
478      "window.domAutomationController.send(window.opener == null);",
479      &success));
480  EXPECT_TRUE(success);
481  EXPECT_FALSE(new_shell->web_contents()->HasOpener());
482
483  // Now navigate forward again (creating a new process) and check opener.
484  NavigateToURL(new_shell, GetCrossSiteURL("files/title1.html"));
485  success = false;
486  EXPECT_TRUE(ExecuteScriptAndExtractBool(
487      new_shell->web_contents(),
488      "window.domAutomationController.send(window.opener == null);",
489      &success));
490  EXPECT_TRUE(success);
491  EXPECT_FALSE(new_shell->web_contents()->HasOpener());
492}
493
494// Test that subframes can disown their openers.  http://crbug.com/225528.
495IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, DisownSubframeOpener) {
496  const GURL frame_url("data:text/html,<iframe name=\"foo\"></iframe>");
497  NavigateToURL(shell(), frame_url);
498
499  // Give the frame an opener using window.open.
500  EXPECT_TRUE(ExecuteScript(shell()->web_contents(),
501                            "window.open('about:blank','foo');"));
502
503  // Now disown the frame's opener.  Shouldn't crash.
504  EXPECT_TRUE(ExecuteScript(shell()->web_contents(),
505                            "window.frames[0].opener = null;"));
506}
507
508// Test for crbug.com/99202.  PostMessage calls should still work after
509// navigating the source and target windows to different sites.
510// Specifically:
511// 1) Create 3 windows (opener, "foo", and _blank) and send "foo" cross-process.
512// 2) Fail to post a message from "foo" to opener with the wrong target origin.
513// 3) Post a message from "foo" to opener, which replies back to "foo".
514// 4) Post a message from _blank to "foo".
515// 5) Post a message from "foo" to a subframe of opener, which replies back.
516// 6) Post a message from _blank to a subframe of "foo".
517IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
518                       SupportCrossProcessPostMessage) {
519  StartServer();
520
521  // Load a page with links that open in a new window.
522  std::string replacement_path;
523  ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
524      "files/click-noreferrer-links.html",
525      foo_host_port_,
526      &replacement_path));
527  NavigateToURL(shell(), test_server()->GetURL(replacement_path));
528
529  // Get the original SiteInstance and RVHM for later comparison.
530  WebContents* opener_contents = shell()->web_contents();
531  scoped_refptr<SiteInstance> orig_site_instance(
532      opener_contents->GetSiteInstance());
533  EXPECT_TRUE(orig_site_instance.get() != NULL);
534  RenderFrameHostManager* opener_manager = static_cast<WebContentsImpl*>(
535      opener_contents)->GetRenderManagerForTesting();
536
537  // 1) Open two more windows, one named.  These initially have openers but no
538  // reference to each other.  We will later post a message between them.
539
540  // First, a named target=foo window.
541  ShellAddedObserver new_shell_observer;
542  bool success = false;
543  EXPECT_TRUE(ExecuteScriptAndExtractBool(
544      opener_contents,
545      "window.domAutomationController.send(clickSameSiteTargetedLink());",
546      &success));
547  EXPECT_TRUE(success);
548  Shell* new_shell = new_shell_observer.GetShell();
549
550  // Wait for the navigation in the new window to finish, if it hasn't, then
551  // send it to post_message.html on a different site.
552  WebContents* foo_contents = new_shell->web_contents();
553  WaitForLoadStop(foo_contents);
554  EXPECT_EQ("/files/navigate_opener.html",
555            foo_contents->GetLastCommittedURL().path());
556  NavigateToURL(new_shell, GetCrossSiteURL("files/post_message.html"));
557  scoped_refptr<SiteInstance> foo_site_instance(
558      foo_contents->GetSiteInstance());
559  EXPECT_NE(orig_site_instance, foo_site_instance);
560
561  // Second, a target=_blank window.
562  ShellAddedObserver new_shell_observer2;
563  EXPECT_TRUE(ExecuteScriptAndExtractBool(
564      shell()->web_contents(),
565      "window.domAutomationController.send(clickSameSiteTargetBlankLink());",
566      &success));
567  EXPECT_TRUE(success);
568
569  // Wait for the navigation in the new window to finish, if it hasn't, then
570  // send it to post_message.html on the original site.
571  Shell* new_shell2 = new_shell_observer2.GetShell();
572  WebContents* new_contents = new_shell2->web_contents();
573  WaitForLoadStop(new_contents);
574  EXPECT_EQ("/files/title2.html", new_contents->GetLastCommittedURL().path());
575  NavigateToURL(new_shell2, test_server()->GetURL("files/post_message.html"));
576  EXPECT_EQ(orig_site_instance, new_contents->GetSiteInstance());
577  RenderFrameHostManager* new_manager =
578      static_cast<WebContentsImpl*>(new_contents)->GetRenderManagerForTesting();
579
580  // We now have three windows.  The opener should have a swapped out RVH
581  // for the new SiteInstance, but the _blank window should not.
582  EXPECT_EQ(3u, Shell::windows().size());
583  EXPECT_TRUE(
584      opener_manager->GetSwappedOutRenderViewHost(foo_site_instance.get()));
585  EXPECT_FALSE(
586      new_manager->GetSwappedOutRenderViewHost(foo_site_instance.get()));
587
588  // 2) Fail to post a message from the foo window to the opener if the target
589  // origin is wrong.  We won't see an error, but we can check for the right
590  // number of received messages below.
591  EXPECT_TRUE(ExecuteScriptAndExtractBool(
592      foo_contents,
593      "window.domAutomationController.send(postToOpener('msg',"
594      "    'http://google.com'));",
595      &success));
596  EXPECT_TRUE(success);
597  ASSERT_FALSE(
598      opener_manager->GetSwappedOutRenderViewHost(orig_site_instance.get()));
599
600  // 3) Post a message from the foo window to the opener.  The opener will
601  // reply, causing the foo window to update its own title.
602  base::string16 expected_title = ASCIIToUTF16("msg");
603  TitleWatcher title_watcher(foo_contents, expected_title);
604  EXPECT_TRUE(ExecuteScriptAndExtractBool(
605      foo_contents,
606      "window.domAutomationController.send(postToOpener('msg','*'));",
607      &success));
608  EXPECT_TRUE(success);
609  ASSERT_FALSE(
610      opener_manager->GetSwappedOutRenderViewHost(orig_site_instance.get()));
611  ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
612
613  // We should have received only 1 message in the opener and "foo" tabs,
614  // and updated the title.
615  int opener_received_messages = 0;
616  EXPECT_TRUE(ExecuteScriptAndExtractInt(
617      opener_contents,
618      "window.domAutomationController.send(window.receivedMessages);",
619      &opener_received_messages));
620  int foo_received_messages = 0;
621  EXPECT_TRUE(ExecuteScriptAndExtractInt(
622      foo_contents,
623      "window.domAutomationController.send(window.receivedMessages);",
624      &foo_received_messages));
625  EXPECT_EQ(1, foo_received_messages);
626  EXPECT_EQ(1, opener_received_messages);
627  EXPECT_EQ(ASCIIToUTF16("msg"), foo_contents->GetTitle());
628
629  // 4) Now post a message from the _blank window to the foo window.  The
630  // foo window will update its title and will not reply.
631  expected_title = ASCIIToUTF16("msg2");
632  TitleWatcher title_watcher2(foo_contents, expected_title);
633  EXPECT_TRUE(ExecuteScriptAndExtractBool(
634      new_contents,
635      "window.domAutomationController.send(postToFoo('msg2'));",
636      &success));
637  EXPECT_TRUE(success);
638  ASSERT_EQ(expected_title, title_watcher2.WaitAndGetTitle());
639
640  // This postMessage should have created a swapped out RVH for the new
641  // SiteInstance in the target=_blank window.
642  EXPECT_TRUE(
643      new_manager->GetSwappedOutRenderViewHost(foo_site_instance.get()));
644
645  // TODO(nasko): Test subframe targeting of postMessage once
646  // http://crbug.com/153701 is fixed.
647}
648
649// Test for crbug.com/278336. MessagePorts should work cross-process. I.e.,
650// messages which contain Transferables and get intercepted by
651// RenderViewImpl::willCheckAndDispatchMessageEvent (because the RenderView is
652// swapped out) should work.
653// Specifically:
654// 1) Create 2 windows (opener and "foo") and send "foo" cross-process.
655// 2) Post a message containing a message port from opener to "foo".
656// 3) Post a message from "foo" back to opener via the passed message port.
657// The test will be enabled when the feature implementation lands.
658IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
659                       SupportCrossProcessPostMessageWithMessagePort) {
660  StartServer();
661
662  // Load a page with links that open in a new window.
663  std::string replacement_path;
664  ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
665      "files/click-noreferrer-links.html",
666      foo_host_port_,
667      &replacement_path));
668  NavigateToURL(shell(), test_server()->GetURL(replacement_path));
669
670  // Get the original SiteInstance and RVHM for later comparison.
671  WebContents* opener_contents = shell()->web_contents();
672  scoped_refptr<SiteInstance> orig_site_instance(
673      opener_contents->GetSiteInstance());
674  EXPECT_TRUE(orig_site_instance.get() != NULL);
675  RenderFrameHostManager* opener_manager = static_cast<WebContentsImpl*>(
676      opener_contents)->GetRenderManagerForTesting();
677
678  // 1) Open a named target=foo window. We will later post a message between the
679  // opener and the new window.
680  ShellAddedObserver new_shell_observer;
681  bool success = false;
682  EXPECT_TRUE(ExecuteScriptAndExtractBool(
683      opener_contents,
684      "window.domAutomationController.send(clickSameSiteTargetedLink());",
685      &success));
686  EXPECT_TRUE(success);
687  Shell* new_shell = new_shell_observer.GetShell();
688
689  // Wait for the navigation in the new window to finish, if it hasn't, then
690  // send it to post_message.html on a different site.
691  WebContents* foo_contents = new_shell->web_contents();
692  WaitForLoadStop(foo_contents);
693  EXPECT_EQ("/files/navigate_opener.html",
694            foo_contents->GetLastCommittedURL().path());
695  NavigateToURL(new_shell, GetCrossSiteURL("files/post_message.html"));
696  scoped_refptr<SiteInstance> foo_site_instance(
697      foo_contents->GetSiteInstance());
698  EXPECT_NE(orig_site_instance, foo_site_instance);
699
700  // We now have two windows. The opener should have a swapped out RVH
701  // for the new SiteInstance.
702  EXPECT_EQ(2u, Shell::windows().size());
703  EXPECT_TRUE(
704      opener_manager->GetSwappedOutRenderViewHost(foo_site_instance.get()));
705
706  // 2) Post a message containing a MessagePort from opener to the the foo
707  // window. The foo window will reply via the passed port, causing the opener
708  // to update its own title.
709  base::string16 expected_title = ASCIIToUTF16("msg-back-via-port");
710  TitleWatcher title_observer(opener_contents, expected_title);
711  EXPECT_TRUE(ExecuteScriptAndExtractBool(
712      opener_contents,
713      "window.domAutomationController.send(postWithPortToFoo());",
714      &success));
715  EXPECT_TRUE(success);
716  ASSERT_FALSE(
717      opener_manager->GetSwappedOutRenderViewHost(orig_site_instance.get()));
718  ASSERT_EQ(expected_title, title_observer.WaitAndGetTitle());
719
720  // Check message counts.
721  int opener_received_messages_via_port = 0;
722  EXPECT_TRUE(ExecuteScriptAndExtractInt(
723      opener_contents,
724      "window.domAutomationController.send(window.receivedMessagesViaPort);",
725      &opener_received_messages_via_port));
726  int foo_received_messages = 0;
727  EXPECT_TRUE(ExecuteScriptAndExtractInt(
728      foo_contents,
729      "window.domAutomationController.send(window.receivedMessages);",
730      &foo_received_messages));
731  int foo_received_messages_with_port = 0;
732  EXPECT_TRUE(ExecuteScriptAndExtractInt(
733      foo_contents,
734      "window.domAutomationController.send(window.receivedMessagesWithPort);",
735      &foo_received_messages_with_port));
736  EXPECT_EQ(1, foo_received_messages);
737  EXPECT_EQ(1, foo_received_messages_with_port);
738  EXPECT_EQ(1, opener_received_messages_via_port);
739  EXPECT_EQ(ASCIIToUTF16("msg-with-port"), foo_contents->GetTitle());
740  EXPECT_EQ(ASCIIToUTF16("msg-back-via-port"), opener_contents->GetTitle());
741}
742
743// Test for crbug.com/116192.  Navigations to a window's opener should
744// still work after a process swap.
745IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
746                       AllowTargetedNavigationsInOpenerAfterSwap) {
747  StartServer();
748
749  // Load a page with links that open in a new window.
750  std::string replacement_path;
751  ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
752      "files/click-noreferrer-links.html",
753      foo_host_port_,
754      &replacement_path));
755  NavigateToURL(shell(), test_server()->GetURL(replacement_path));
756
757  // Get the original tab and SiteInstance for later comparison.
758  WebContents* orig_contents = shell()->web_contents();
759  scoped_refptr<SiteInstance> orig_site_instance(
760      orig_contents->GetSiteInstance());
761  EXPECT_TRUE(orig_site_instance.get() != NULL);
762
763  // Test clicking a target=foo link.
764  ShellAddedObserver new_shell_observer;
765  bool success = false;
766  EXPECT_TRUE(ExecuteScriptAndExtractBool(
767      orig_contents,
768      "window.domAutomationController.send(clickSameSiteTargetedLink());",
769      &success));
770  EXPECT_TRUE(success);
771  Shell* new_shell = new_shell_observer.GetShell();
772
773  // Wait for the navigation in the new window to finish, if it hasn't.
774  WaitForLoadStop(new_shell->web_contents());
775  EXPECT_EQ("/files/navigate_opener.html",
776            new_shell->web_contents()->GetLastCommittedURL().path());
777
778  // Should have the same SiteInstance.
779  scoped_refptr<SiteInstance> blank_site_instance(
780      new_shell->web_contents()->GetSiteInstance());
781  EXPECT_EQ(orig_site_instance, blank_site_instance);
782
783  // Now navigate the original (opener) tab to a different site.
784  NavigateToURL(shell(), GetCrossSiteURL("files/title1.html"));
785  scoped_refptr<SiteInstance> new_site_instance(
786      shell()->web_contents()->GetSiteInstance());
787  EXPECT_NE(orig_site_instance, new_site_instance);
788
789  // The opened tab should be able to navigate the opener back to its process.
790  TestNavigationObserver navigation_observer(orig_contents);
791  EXPECT_TRUE(ExecuteScriptAndExtractBool(
792      new_shell->web_contents(),
793      "window.domAutomationController.send(navigateOpener());",
794      &success));
795  EXPECT_TRUE(success);
796  navigation_observer.Wait();
797
798  // Should have swapped back into this process.
799  scoped_refptr<SiteInstance> revisit_site_instance(
800      shell()->web_contents()->GetSiteInstance());
801  EXPECT_EQ(orig_site_instance, revisit_site_instance);
802}
803
804// Test that opening a new window in the same SiteInstance and then navigating
805// both windows to a different SiteInstance allows the first process to exit.
806// See http://crbug.com/126333.
807IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
808                       ProcessExitWithSwappedOutViews) {
809  StartServer();
810
811  // Load a page with links that open in a new window.
812  std::string replacement_path;
813  ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
814      "files/click-noreferrer-links.html",
815      foo_host_port_,
816      &replacement_path));
817  NavigateToURL(shell(), test_server()->GetURL(replacement_path));
818
819  // Get the original SiteInstance for later comparison.
820  scoped_refptr<SiteInstance> orig_site_instance(
821      shell()->web_contents()->GetSiteInstance());
822  EXPECT_TRUE(orig_site_instance.get() != NULL);
823
824  // Test clicking a target=foo link.
825  ShellAddedObserver new_shell_observer;
826  bool success = false;
827  EXPECT_TRUE(ExecuteScriptAndExtractBool(
828      shell()->web_contents(),
829      "window.domAutomationController.send(clickSameSiteTargetedLink());",
830      &success));
831  EXPECT_TRUE(success);
832  Shell* new_shell = new_shell_observer.GetShell();
833
834  // Wait for the navigation in the new window to finish, if it hasn't.
835  WaitForLoadStop(new_shell->web_contents());
836  EXPECT_EQ("/files/navigate_opener.html",
837            new_shell->web_contents()->GetLastCommittedURL().path());
838
839  // Should have the same SiteInstance.
840  scoped_refptr<SiteInstance> opened_site_instance(
841      new_shell->web_contents()->GetSiteInstance());
842  EXPECT_EQ(orig_site_instance, opened_site_instance);
843
844  // Now navigate the opened window to a different site.
845  NavigateToURL(new_shell, GetCrossSiteURL("files/title1.html"));
846  scoped_refptr<SiteInstance> new_site_instance(
847      new_shell->web_contents()->GetSiteInstance());
848  EXPECT_NE(orig_site_instance, new_site_instance);
849
850  // The original process should still be alive, since it is still used in the
851  // first window.
852  RenderProcessHost* orig_process = orig_site_instance->GetProcess();
853  EXPECT_TRUE(orig_process->HasConnection());
854
855  // Navigate the first window to a different site as well.  The original
856  // process should exit, since all of its views are now swapped out.
857  RenderProcessHostWatcher exit_observer(
858      orig_process,
859      RenderProcessHostWatcher::WATCH_FOR_HOST_DESTRUCTION);
860  NavigateToURL(shell(), GetCrossSiteURL("files/title1.html"));
861  exit_observer.Wait();
862  scoped_refptr<SiteInstance> new_site_instance2(
863      shell()->web_contents()->GetSiteInstance());
864  EXPECT_EQ(new_site_instance, new_site_instance2);
865}
866
867// Test for crbug.com/76666.  A cross-site navigation that fails with a 204
868// error should not make us ignore future renderer-initiated navigations.
869IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, ClickLinkAfter204Error) {
870  StartServer();
871
872  // Get the original SiteInstance for later comparison.
873  scoped_refptr<SiteInstance> orig_site_instance(
874      shell()->web_contents()->GetSiteInstance());
875  EXPECT_TRUE(orig_site_instance.get() != NULL);
876
877  // Load a cross-site page that fails with a 204 error.
878  NavigateToURL(shell(), GetCrossSiteURL("nocontent"));
879
880  // We should still be looking at the normal page.  Because we started from a
881  // blank new tab, the typed URL will still be visible until the user clears it
882  // manually.  The last committed URL will be the previous page.
883  scoped_refptr<SiteInstance> post_nav_site_instance(
884      shell()->web_contents()->GetSiteInstance());
885  EXPECT_EQ(orig_site_instance, post_nav_site_instance);
886  EXPECT_EQ("/nocontent",
887            shell()->web_contents()->GetVisibleURL().path());
888  EXPECT_FALSE(
889      shell()->web_contents()->GetController().GetLastCommittedEntry());
890
891  // Renderer-initiated navigations should work.
892  base::string16 expected_title = ASCIIToUTF16("Title Of Awesomeness");
893  TitleWatcher title_watcher(shell()->web_contents(), expected_title);
894  GURL url = test_server()->GetURL("files/title2.html");
895  EXPECT_TRUE(ExecuteScript(
896      shell()->web_contents(),
897      base::StringPrintf("location.href = '%s'", url.spec().c_str())));
898  ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
899
900  // Opens in same tab.
901  EXPECT_EQ(1u, Shell::windows().size());
902  EXPECT_EQ("/files/title2.html",
903            shell()->web_contents()->GetLastCommittedURL().path());
904
905  // Should have the same SiteInstance.
906  scoped_refptr<SiteInstance> new_site_instance(
907      shell()->web_contents()->GetSiteInstance());
908  EXPECT_EQ(orig_site_instance, new_site_instance);
909}
910
911// Test for crbug.com/9682.  We should show the URL for a pending renderer-
912// initiated navigation in a new tab, until the content of the initial
913// about:blank page is modified by another window.  At that point, we should
914// revert to showing about:blank to prevent a URL spoof.
915IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, ShowLoadingURLUntilSpoof) {
916  ASSERT_TRUE(test_server()->Start());
917
918  // Load a page that can open a URL that won't commit in a new window.
919  NavigateToURL(
920      shell(), test_server()->GetURL("files/click-nocontent-link.html"));
921  WebContents* orig_contents = shell()->web_contents();
922
923  // Click a /nocontent link that opens in a new window but never commits.
924  ShellAddedObserver new_shell_observer;
925  bool success = false;
926  EXPECT_TRUE(ExecuteScriptAndExtractBool(
927      orig_contents,
928      "window.domAutomationController.send(clickNoContentTargetedLink());",
929      &success));
930  EXPECT_TRUE(success);
931
932  // Wait for the window to open.
933  Shell* new_shell = new_shell_observer.GetShell();
934
935  // Ensure the destination URL is visible, because it is considered the
936  // initial navigation.
937  WebContents* contents = new_shell->web_contents();
938  EXPECT_TRUE(contents->GetController().IsInitialNavigation());
939  EXPECT_EQ("/nocontent",
940            contents->GetController().GetVisibleEntry()->GetURL().path());
941
942  // Now modify the contents of the new window from the opener.  This will also
943  // modify the title of the document to give us something to listen for.
944  base::string16 expected_title = ASCIIToUTF16("Modified Title");
945  TitleWatcher title_watcher(contents, expected_title);
946  success = false;
947  EXPECT_TRUE(ExecuteScriptAndExtractBool(
948      orig_contents,
949      "window.domAutomationController.send(modifyNewWindow());",
950      &success));
951  EXPECT_TRUE(success);
952  ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
953
954  // At this point, we should no longer be showing the destination URL.
955  // The visible entry should be null, resulting in about:blank in the address
956  // bar.
957  EXPECT_FALSE(contents->GetController().GetVisibleEntry());
958}
959
960// Test for crbug.com/9682.  We should not show the URL for a pending renderer-
961// initiated navigation in a new tab if it is not the initial navigation.  In
962// this case, the renderer will not notify us of a modification, so we cannot
963// show the pending URL without allowing a spoof.
964IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
965                       DontShowLoadingURLIfNotInitialNav) {
966  ASSERT_TRUE(test_server()->Start());
967
968  // Load a page that can open a URL that won't commit in a new window.
969  NavigateToURL(
970      shell(), test_server()->GetURL("files/click-nocontent-link.html"));
971  WebContents* orig_contents = shell()->web_contents();
972
973  // Click a /nocontent link that opens in a new window but never commits.
974  // By using an onclick handler that first creates the window, the slow
975  // navigation is not considered an initial navigation.
976  ShellAddedObserver new_shell_observer;
977  bool success = false;
978  EXPECT_TRUE(ExecuteScriptAndExtractBool(
979      orig_contents,
980      "window.domAutomationController.send("
981      "clickNoContentScriptedTargetedLink());",
982      &success));
983  EXPECT_TRUE(success);
984
985  // Wait for the window to open.
986  Shell* new_shell = new_shell_observer.GetShell();
987
988  // Ensure the destination URL is not visible, because it is not the initial
989  // navigation.
990  WebContents* contents = new_shell->web_contents();
991  EXPECT_FALSE(contents->GetController().IsInitialNavigation());
992  EXPECT_FALSE(contents->GetController().GetVisibleEntry());
993}
994
995// Crashes under ThreadSanitizer, http://crbug.com/356758.
996#if defined(THREAD_SANITIZER)
997#define MAYBE_BackForwardNotStale DISABLED_BackForwardNotStale
998#else
999#define MAYBE_BackForwardNotStale BackForwardNotStale
1000#endif
1001// Test for http://crbug.com/93427.  Ensure that cross-site navigations
1002// do not cause back/forward navigations to be considered stale by the
1003// renderer.
1004IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, MAYBE_BackForwardNotStale) {
1005  StartServer();
1006  NavigateToURL(shell(), GURL(url::kAboutBlankURL));
1007
1008  // Visit a page on first site.
1009  NavigateToURL(shell(), test_server()->GetURL("files/title1.html"));
1010
1011  // Visit three pages on second site.
1012  NavigateToURL(shell(), GetCrossSiteURL("files/title1.html"));
1013  NavigateToURL(shell(), GetCrossSiteURL("files/title2.html"));
1014  NavigateToURL(shell(), GetCrossSiteURL("files/title3.html"));
1015
1016  // History is now [blank, A1, B1, B2, *B3].
1017  WebContents* contents = shell()->web_contents();
1018  EXPECT_EQ(5, contents->GetController().GetEntryCount());
1019
1020  // Open another window in same process to keep this process alive.
1021  Shell* new_shell = CreateBrowser();
1022  NavigateToURL(new_shell, GetCrossSiteURL("files/title1.html"));
1023
1024  // Go back three times to first site.
1025  {
1026    TestNavigationObserver back_nav_load_observer(shell()->web_contents());
1027    shell()->web_contents()->GetController().GoBack();
1028    back_nav_load_observer.Wait();
1029  }
1030  {
1031    TestNavigationObserver back_nav_load_observer(shell()->web_contents());
1032    shell()->web_contents()->GetController().GoBack();
1033    back_nav_load_observer.Wait();
1034  }
1035  {
1036    TestNavigationObserver back_nav_load_observer(shell()->web_contents());
1037    shell()->web_contents()->GetController().GoBack();
1038    back_nav_load_observer.Wait();
1039  }
1040
1041  // Now go forward twice to B2.  Shouldn't be left spinning.
1042  {
1043    TestNavigationObserver forward_nav_load_observer(shell()->web_contents());
1044    shell()->web_contents()->GetController().GoForward();
1045    forward_nav_load_observer.Wait();
1046  }
1047  {
1048    TestNavigationObserver forward_nav_load_observer(shell()->web_contents());
1049    shell()->web_contents()->GetController().GoForward();
1050    forward_nav_load_observer.Wait();
1051  }
1052
1053  // Go back twice to first site.
1054  {
1055    TestNavigationObserver back_nav_load_observer(shell()->web_contents());
1056    shell()->web_contents()->GetController().GoBack();
1057    back_nav_load_observer.Wait();
1058  }
1059  {
1060    TestNavigationObserver back_nav_load_observer(shell()->web_contents());
1061    shell()->web_contents()->GetController().GoBack();
1062    back_nav_load_observer.Wait();
1063  }
1064
1065  // Now go forward directly to B3.  Shouldn't be left spinning.
1066  {
1067    TestNavigationObserver forward_nav_load_observer(shell()->web_contents());
1068    shell()->web_contents()->GetController().GoToIndex(4);
1069    forward_nav_load_observer.Wait();
1070  }
1071}
1072
1073// Test for http://crbug.com/130016.
1074// Swapping out a render view should update its visiblity state.
1075IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
1076                       SwappedOutViewHasCorrectVisibilityState) {
1077  StartServer();
1078
1079  // Load a page with links that open in a new window.
1080  std::string replacement_path;
1081  ASSERT_TRUE(GetFilePathWithHostAndPortReplacement(
1082      "files/click-noreferrer-links.html",
1083      foo_host_port_,
1084      &replacement_path));
1085  NavigateToURL(shell(), test_server()->GetURL(replacement_path));
1086
1087  // Open a same-site link in a new widnow.
1088  ShellAddedObserver new_shell_observer;
1089  bool success = false;
1090  EXPECT_TRUE(ExecuteScriptAndExtractBool(
1091      shell()->web_contents(),
1092      "window.domAutomationController.send(clickSameSiteTargetedLink());",
1093      &success));
1094  EXPECT_TRUE(success);
1095  Shell* new_shell = new_shell_observer.GetShell();
1096
1097  // Wait for the navigation in the new tab to finish, if it hasn't.
1098  WaitForLoadStop(new_shell->web_contents());
1099  EXPECT_EQ("/files/navigate_opener.html",
1100            new_shell->web_contents()->GetLastCommittedURL().path());
1101
1102  RenderViewHost* rvh = new_shell->web_contents()->GetRenderViewHost();
1103
1104  EXPECT_TRUE(ExecuteScriptAndExtractBool(
1105      rvh,
1106      "window.domAutomationController.send("
1107      "    document.visibilityState == 'visible');",
1108      &success));
1109  EXPECT_TRUE(success);
1110
1111  // Now navigate the new window to a different site. This should swap out the
1112  // tab's existing RenderView, causing it become hidden.
1113  NavigateToURL(new_shell, GetCrossSiteURL("files/title1.html"));
1114
1115  EXPECT_TRUE(ExecuteScriptAndExtractBool(
1116      rvh,
1117      "window.domAutomationController.send("
1118      "    document.visibilityState == 'hidden');",
1119      &success));
1120  EXPECT_TRUE(success);
1121
1122  // Going back should make the previously swapped-out view to become visible
1123  // again.
1124  {
1125    TestNavigationObserver back_nav_load_observer(new_shell->web_contents());
1126    new_shell->web_contents()->GetController().GoBack();
1127    back_nav_load_observer.Wait();
1128  }
1129
1130  EXPECT_EQ("/files/navigate_opener.html",
1131            new_shell->web_contents()->GetLastCommittedURL().path());
1132
1133  EXPECT_EQ(rvh, new_shell->web_contents()->GetRenderViewHost());
1134
1135  EXPECT_TRUE(ExecuteScriptAndExtractBool(
1136      rvh,
1137      "window.domAutomationController.send("
1138      "    document.visibilityState == 'visible');",
1139      &success));
1140  EXPECT_TRUE(success);
1141}
1142
1143// This class ensures that all the given RenderViewHosts have properly been
1144// shutdown.
1145class RenderViewHostDestructionObserver : public WebContentsObserver {
1146 public:
1147  explicit RenderViewHostDestructionObserver(WebContents* web_contents)
1148      : WebContentsObserver(web_contents) {}
1149  virtual ~RenderViewHostDestructionObserver() {}
1150  void EnsureRVHGetsDestructed(RenderViewHost* rvh) {
1151    watched_render_view_hosts_.insert(rvh);
1152  }
1153  size_t GetNumberOfWatchedRenderViewHosts() const {
1154    return watched_render_view_hosts_.size();
1155  }
1156
1157 private:
1158  // WebContentsObserver implementation:
1159  virtual void RenderViewDeleted(RenderViewHost* rvh) OVERRIDE {
1160    watched_render_view_hosts_.erase(rvh);
1161  }
1162
1163  std::set<RenderViewHost*> watched_render_view_hosts_;
1164};
1165
1166// Crashes under ThreadSanitizer, http://crbug.com/356758.
1167#if defined(THREAD_SANITIZER)
1168#define MAYBE_LeakingRenderViewHosts DISABLED_LeakingRenderViewHosts
1169#else
1170#define MAYBE_LeakingRenderViewHosts LeakingRenderViewHosts
1171#endif
1172// Test for crbug.com/90867. Make sure we don't leak render view hosts since
1173// they may cause crashes or memory corruptions when trying to call dead
1174// delegate_. This test also verifies crbug.com/117420 and crbug.com/143255 to
1175// ensure that a separate SiteInstance is created when navigating to view-source
1176// URLs, regardless of current URL.
1177IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
1178                       MAYBE_LeakingRenderViewHosts) {
1179  StartServer();
1180
1181  // Observe the created render_view_host's to make sure they will not leak.
1182  RenderViewHostDestructionObserver rvh_observers(shell()->web_contents());
1183
1184  GURL navigated_url(test_server()->GetURL("files/title2.html"));
1185  GURL view_source_url(kViewSourceScheme + std::string(":") +
1186                       navigated_url.spec());
1187
1188  // Let's ensure that when we start with a blank window, navigating away to a
1189  // view-source URL, we create a new SiteInstance.
1190  RenderViewHost* blank_rvh = shell()->web_contents()->GetRenderViewHost();
1191  SiteInstance* blank_site_instance = blank_rvh->GetSiteInstance();
1192  EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), GURL::EmptyGURL());
1193  EXPECT_EQ(blank_site_instance->GetSiteURL(), GURL::EmptyGURL());
1194  rvh_observers.EnsureRVHGetsDestructed(blank_rvh);
1195
1196  // Now navigate to the view-source URL and ensure we got a different
1197  // SiteInstance and RenderViewHost.
1198  NavigateToURL(shell(), view_source_url);
1199  EXPECT_NE(blank_rvh, shell()->web_contents()->GetRenderViewHost());
1200  EXPECT_NE(blank_site_instance, shell()->web_contents()->
1201      GetRenderViewHost()->GetSiteInstance());
1202  rvh_observers.EnsureRVHGetsDestructed(
1203      shell()->web_contents()->GetRenderViewHost());
1204
1205  // Load a random page and then navigate to view-source: of it.
1206  // This used to cause two RVH instances for the same SiteInstance, which
1207  // was a problem.  This is no longer the case.
1208  NavigateToURL(shell(), navigated_url);
1209  SiteInstance* site_instance1 = shell()->web_contents()->
1210      GetRenderViewHost()->GetSiteInstance();
1211  rvh_observers.EnsureRVHGetsDestructed(
1212      shell()->web_contents()->GetRenderViewHost());
1213
1214  NavigateToURL(shell(), view_source_url);
1215  rvh_observers.EnsureRVHGetsDestructed(
1216      shell()->web_contents()->GetRenderViewHost());
1217  SiteInstance* site_instance2 = shell()->web_contents()->
1218      GetRenderViewHost()->GetSiteInstance();
1219
1220  // Ensure that view-source navigations force a new SiteInstance.
1221  EXPECT_NE(site_instance1, site_instance2);
1222
1223  // Now navigate to a different instance so that we swap out again.
1224  NavigateToURL(shell(), GetCrossSiteURL("files/title2.html"));
1225  rvh_observers.EnsureRVHGetsDestructed(
1226      shell()->web_contents()->GetRenderViewHost());
1227
1228  // This used to leak a render view host.
1229  shell()->Close();
1230
1231  RunAllPendingInMessageLoop();  // Needed on ChromeOS.
1232
1233  EXPECT_EQ(0U, rvh_observers.GetNumberOfWatchedRenderViewHosts());
1234}
1235
1236// Test for crbug.com/143155.  Frame tree updates during unload should not
1237// interrupt the intended navigation and show swappedout:// instead.
1238// Specifically:
1239// 1) Open 2 tabs in an HTTP SiteInstance, with a subframe in the opener.
1240// 2) Send the second tab to a different foo.com SiteInstance.
1241//    This creates a swapped out opener for the first tab in the foo process.
1242// 3) Navigate the first tab to the foo.com SiteInstance, and have the first
1243//    tab's unload handler remove its frame.
1244// This used to cause an update to the frame tree of the swapped out RV,
1245// just as it was navigating to a real page.  That pre-empted the real
1246// navigation and visibly sent the tab to swappedout://.
1247IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
1248                       DontPreemptNavigationWithFrameTreeUpdate) {
1249  StartServer();
1250
1251  // 1. Load a page that deletes its iframe during unload.
1252  NavigateToURL(shell(),
1253                test_server()->GetURL("files/remove_frame_on_unload.html"));
1254
1255  // Get the original SiteInstance for later comparison.
1256  scoped_refptr<SiteInstance> orig_site_instance(
1257      shell()->web_contents()->GetSiteInstance());
1258
1259  // Open a same-site page in a new window.
1260  ShellAddedObserver new_shell_observer;
1261  bool success = false;
1262  EXPECT_TRUE(ExecuteScriptAndExtractBool(
1263      shell()->web_contents(),
1264      "window.domAutomationController.send(openWindow());",
1265      &success));
1266  EXPECT_TRUE(success);
1267  Shell* new_shell = new_shell_observer.GetShell();
1268
1269  // Wait for the navigation in the new window to finish, if it hasn't.
1270  WaitForLoadStop(new_shell->web_contents());
1271  EXPECT_EQ("/files/title1.html",
1272            new_shell->web_contents()->GetLastCommittedURL().path());
1273
1274  // Should have the same SiteInstance.
1275  EXPECT_EQ(orig_site_instance, new_shell->web_contents()->GetSiteInstance());
1276
1277  // 2. Send the second tab to a different process.
1278  NavigateToURL(new_shell, GetCrossSiteURL("files/title1.html"));
1279  scoped_refptr<SiteInstance> new_site_instance(
1280      new_shell->web_contents()->GetSiteInstance());
1281  EXPECT_NE(orig_site_instance, new_site_instance);
1282
1283  // 3. Send the first tab to the second tab's process.
1284  NavigateToURL(shell(), GetCrossSiteURL("files/title1.html"));
1285
1286  // Make sure it ends up at the right page.
1287  WaitForLoadStop(shell()->web_contents());
1288  EXPECT_EQ(GetCrossSiteURL("files/title1.html"),
1289            shell()->web_contents()->GetLastCommittedURL());
1290  EXPECT_EQ(new_site_instance, shell()->web_contents()->GetSiteInstance());
1291}
1292
1293// Ensure that renderer-side debug URLs do not cause a process swap, since they
1294// are meant to run in the current page.  We had a bug where we expected a
1295// BrowsingInstance swap to occur on pages like view-source and extensions,
1296// which broke chrome://crash and javascript: URLs.
1297// See http://crbug.com/335503.
1298IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, RendererDebugURLsDontSwap) {
1299  ASSERT_TRUE(test_server()->Start());
1300
1301  GURL original_url(test_server()->GetURL("files/title2.html"));
1302  GURL view_source_url(kViewSourceScheme + std::string(":") +
1303                       original_url.spec());
1304
1305  NavigateToURL(shell(), view_source_url);
1306
1307  // Check that javascript: URLs work.
1308  base::string16 expected_title = ASCIIToUTF16("msg");
1309  TitleWatcher title_watcher(shell()->web_contents(), expected_title);
1310  shell()->LoadURL(GURL("javascript:document.title='msg'"));
1311  ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
1312
1313  // Crash the renderer of the view-source page.
1314  RenderProcessHostWatcher crash_observer(
1315      shell()->web_contents(),
1316      RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
1317  NavigateToURL(shell(), GURL(kChromeUICrashURL));
1318  crash_observer.Wait();
1319}
1320
1321// Ensure that renderer-side debug URLs don't take effect on crashed renderers.
1322// Otherwise, we might try to load an unprivileged about:blank page into a
1323// WebUI-enabled RenderProcessHost, failing a safety check in InitRenderView.
1324// See http://crbug.com/334214.
1325IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
1326                       IgnoreRendererDebugURLsWhenCrashed) {
1327  // Visit a WebUI page with bindings.
1328  GURL webui_url = GURL(std::string(kChromeUIScheme) + "://" +
1329                        std::string(kChromeUIGpuHost));
1330  NavigateToURL(shell(), webui_url);
1331  EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
1332                  shell()->web_contents()->GetRenderProcessHost()->GetID()));
1333
1334  // Crash the renderer of the WebUI page.
1335  RenderProcessHostWatcher crash_observer(
1336      shell()->web_contents(),
1337      RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
1338  NavigateToURL(shell(), GURL(kChromeUICrashURL));
1339  crash_observer.Wait();
1340
1341  // Load the crash URL again but don't wait for any action.  If it is not
1342  // ignored this time, we will fail the WebUI CHECK in InitRenderView.
1343  shell()->LoadURL(GURL(kChromeUICrashURL));
1344
1345  // Ensure that such URLs can still work as the initial navigation of a tab.
1346  // We postpone the initial navigation of the tab using an empty GURL, so that
1347  // we can add a watcher for crashes.
1348  Shell* shell2 = Shell::CreateNewWindow(
1349      shell()->web_contents()->GetBrowserContext(), GURL(), NULL,
1350      MSG_ROUTING_NONE, gfx::Size());
1351  RenderProcessHostWatcher crash_observer2(
1352      shell2->web_contents(),
1353      RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
1354  NavigateToURL(shell2, GURL(kChromeUIKillURL));
1355  crash_observer2.Wait();
1356}
1357
1358// Ensure that pending_and_current_web_ui_ is cleared when a URL commits.
1359// Otherwise it might get picked up by InitRenderView when granting bindings
1360// to other RenderViewHosts.  See http://crbug.com/330811.
1361IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, ClearPendingWebUIOnCommit) {
1362  // Visit a WebUI page with bindings.
1363  GURL webui_url(GURL(std::string(kChromeUIScheme) + "://" +
1364                      std::string(kChromeUIGpuHost)));
1365  NavigateToURL(shell(), webui_url);
1366  EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
1367                  shell()->web_contents()->GetRenderProcessHost()->GetID()));
1368  WebContentsImpl* web_contents = static_cast<WebContentsImpl*>(
1369      shell()->web_contents());
1370  WebUIImpl* webui = web_contents->GetRenderManagerForTesting()->web_ui();
1371  EXPECT_TRUE(webui);
1372  EXPECT_FALSE(web_contents->GetRenderManagerForTesting()->pending_web_ui());
1373
1374  // Navigate to another WebUI URL that reuses the WebUI object.  Make sure we
1375  // clear pending_web_ui() when it commits.
1376  GURL webui_url2(webui_url.spec() + "#foo");
1377  NavigateToURL(shell(), webui_url2);
1378  EXPECT_EQ(webui, web_contents->GetRenderManagerForTesting()->web_ui());
1379  EXPECT_FALSE(web_contents->GetRenderManagerForTesting()->pending_web_ui());
1380}
1381
1382class RFHMProcessPerTabTest : public RenderFrameHostManagerTest {
1383 public:
1384  RFHMProcessPerTabTest() {}
1385
1386  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
1387    command_line->AppendSwitch(switches::kProcessPerTab);
1388  }
1389};
1390
1391// Test that we still swap processes for BrowsingInstance changes even in
1392// --process-per-tab mode.  See http://crbug.com/343017.
1393// Disabled on Android: http://crbug.com/345873.
1394// Crashes under ThreadSanitizer, http://crbug.com/356758.
1395#if defined(OS_ANDROID) || defined(THREAD_SANITIZER)
1396#define MAYBE_BackFromWebUI DISABLED_BackFromWebUI
1397#else
1398#define MAYBE_BackFromWebUI BackFromWebUI
1399#endif
1400IN_PROC_BROWSER_TEST_F(RFHMProcessPerTabTest, MAYBE_BackFromWebUI) {
1401  ASSERT_TRUE(test_server()->Start());
1402  GURL original_url(test_server()->GetURL("files/title2.html"));
1403  NavigateToURL(shell(), original_url);
1404
1405  // Visit a WebUI page with bindings.
1406  GURL webui_url(GURL(std::string(kChromeUIScheme) + "://" +
1407                      std::string(kChromeUIGpuHost)));
1408  NavigateToURL(shell(), webui_url);
1409  EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
1410                  shell()->web_contents()->GetRenderProcessHost()->GetID()));
1411
1412  // Go back and ensure we have no WebUI bindings.
1413  TestNavigationObserver back_nav_load_observer(shell()->web_contents());
1414  shell()->web_contents()->GetController().GoBack();
1415  back_nav_load_observer.Wait();
1416  EXPECT_EQ(original_url, shell()->web_contents()->GetLastCommittedURL());
1417  EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
1418                  shell()->web_contents()->GetRenderProcessHost()->GetID()));
1419}
1420
1421// crbug.com/372360
1422// The test loads url1, opens a link pointing to url2 in a new tab, and
1423// navigates the new tab to url1.
1424// The following is needed for the bug to happen:
1425//  - url1 must require webui bindings;
1426//  - navigating to url2 in the site instance of url1 should not swap
1427//   browsing instances, but should require a new site instance.
1428IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest, WebUIGetsBindings) {
1429  GURL url1(std::string(kChromeUIScheme) + "://" +
1430            std::string(kChromeUIGpuHost));
1431  GURL url2(std::string(kChromeUIScheme) + "://" +
1432            std::string(kChromeUIAccessibilityHost));
1433
1434  // Visit a WebUI page with bindings.
1435  NavigateToURL(shell(), url1);
1436  EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
1437                  shell()->web_contents()->GetRenderProcessHost()->GetID()));
1438  SiteInstance* site_instance1 = shell()->web_contents()->GetSiteInstance();
1439
1440  // Open a new tab. Initially it gets a render view in the original tab's
1441  // current site instance.
1442  TestNavigationObserver nav_observer(NULL);
1443  nav_observer.StartWatchingNewWebContents();
1444  ShellAddedObserver shao;
1445  OpenUrlViaClickTarget(shell()->web_contents(), url2);
1446  nav_observer.Wait();
1447  Shell* new_shell = shao.GetShell();
1448  WebContentsImpl* new_web_contents = static_cast<WebContentsImpl*>(
1449      new_shell->web_contents());
1450  SiteInstance* site_instance2 = new_web_contents->GetSiteInstance();
1451
1452  EXPECT_NE(site_instance2, site_instance1);
1453  EXPECT_TRUE(site_instance2->IsRelatedSiteInstance(site_instance1));
1454  RenderViewHost* initial_rvh = new_web_contents->
1455      GetRenderManagerForTesting()->GetSwappedOutRenderViewHost(site_instance1);
1456  ASSERT_TRUE(initial_rvh);
1457  // The following condition is what was causing the bug.
1458  EXPECT_EQ(0, initial_rvh->GetEnabledBindings());
1459
1460  // Navigate to url1 and check bindings.
1461  NavigateToURL(new_shell, url1);
1462  // The navigation should have used the first SiteInstance, otherwise
1463  // |initial_rvh| did not have a chance to be used.
1464  EXPECT_EQ(new_web_contents->GetSiteInstance(), site_instance1);
1465  EXPECT_EQ(BINDINGS_POLICY_WEB_UI,
1466      new_web_contents->GetRenderViewHost()->GetEnabledBindings());
1467}
1468
1469}  // namespace content
1470