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