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