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 "content/browser/renderer_host/render_process_host_impl.h" 6 #include "content/common/child_process_messages.h" 7 #include "content/public/browser/render_process_host.h" 8 #include "content/public/browser/render_process_host_observer.h" 9 #include "content/public/browser/render_view_host.h" 10 #include "content/public/browser/web_contents.h" 11 #include "content/public/common/url_constants.h" 12 #include "content/public/test/content_browser_test.h" 13 #include "content/public/test/content_browser_test_utils.h" 14 #include "content/shell/browser/shell.h" 15 #include "net/test/embedded_test_server/embedded_test_server.h" 16 17 namespace content { 18 namespace { 19 20 int RenderProcessHostCount() { 21 content::RenderProcessHost::iterator hosts = 22 content::RenderProcessHost::AllHostsIterator(); 23 int count = 0; 24 while (!hosts.IsAtEnd()) { 25 if (hosts.GetCurrentValue()->HasConnection()) 26 count++; 27 hosts.Advance(); 28 } 29 return count; 30 } 31 32 class RenderProcessHostTest : public ContentBrowserTest, 33 public RenderProcessHostObserver { 34 public: 35 RenderProcessHostTest() : process_exits_(0), host_destructions_(0) {} 36 37 protected: 38 // RenderProcessHostObserver: 39 virtual void RenderProcessExited(RenderProcessHost* host, 40 base::ProcessHandle handle, 41 base::TerminationStatus status, 42 int exit_code) OVERRIDE { 43 ++process_exits_; 44 } 45 virtual void RenderProcessHostDestroyed(RenderProcessHost* host) OVERRIDE { 46 ++host_destructions_; 47 } 48 49 int process_exits_; 50 int host_destructions_; 51 }; 52 53 // Sometimes the renderer process's ShutdownRequest (corresponding to the 54 // ViewMsg_WasSwappedOut from a previous navigation) doesn't arrive until after 55 // the browser process decides to re-use the renderer for a new purpose. This 56 // test makes sure the browser doesn't let the renderer die in that case. See 57 // http://crbug.com/87176. 58 IN_PROC_BROWSER_TEST_F(RenderProcessHostTest, 59 ShutdownRequestFromActiveTabIgnored) { 60 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 61 62 GURL test_url = embedded_test_server()->GetURL("/simple_page.html"); 63 NavigateToURL(shell(), test_url); 64 RenderProcessHost* rph = 65 shell()->web_contents()->GetRenderViewHost()->GetProcess(); 66 67 host_destructions_ = 0; 68 process_exits_ = 0; 69 rph->AddObserver(this); 70 ChildProcessHostMsg_ShutdownRequest msg; 71 rph->OnMessageReceived(msg); 72 73 // If the RPH sends a mistaken ChildProcessMsg_Shutdown, the renderer process 74 // will take some time to die. Wait for a second tab to load in order to give 75 // that time to happen. 76 NavigateToURL(CreateBrowser(), test_url); 77 78 EXPECT_EQ(0, process_exits_); 79 if (!host_destructions_) 80 rph->RemoveObserver(this); 81 } 82 83 IN_PROC_BROWSER_TEST_F(RenderProcessHostTest, 84 GuestsAreNotSuitableHosts) { 85 // Set max renderers to 1 to force running out of processes. 86 content::RenderProcessHost::SetMaxRendererProcessCount(1); 87 88 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 89 90 GURL test_url = embedded_test_server()->GetURL("/simple_page.html"); 91 NavigateToURL(shell(), test_url); 92 RenderProcessHost* rph = 93 shell()->web_contents()->GetRenderViewHost()->GetProcess(); 94 // Make it believe it's a guest. 95 reinterpret_cast<RenderProcessHostImpl*>(rph)-> 96 set_is_isolated_guest_for_testing(true); 97 EXPECT_EQ(1, RenderProcessHostCount()); 98 99 // Navigate to a different page. 100 GURL::Replacements replace_host; 101 std::string host_str("localhost"); // Must stay in scope with replace_host. 102 replace_host.SetHostStr(host_str); 103 GURL another_url = embedded_test_server()->GetURL("/simple_page.html"); 104 another_url = another_url.ReplaceComponents(replace_host); 105 NavigateToURL(CreateBrowser(), another_url); 106 107 // Expect that we got another process (the guest renderer was not reused). 108 EXPECT_EQ(2, RenderProcessHostCount()); 109 } 110 111 class ShellCloser : public RenderProcessHostObserver { 112 public: 113 ShellCloser(Shell* shell, std::string* logging_string) 114 : shell_(shell), logging_string_(logging_string) {} 115 116 protected: 117 // RenderProcessHostObserver: 118 virtual void RenderProcessExited(RenderProcessHost* host, 119 base::ProcessHandle handle, 120 base::TerminationStatus status, 121 int exit_code) OVERRIDE { 122 logging_string_->append("ShellCloser::RenderProcessExited "); 123 shell_->Close(); 124 } 125 126 virtual void RenderProcessHostDestroyed(RenderProcessHost* host) OVERRIDE { 127 logging_string_->append("ShellCloser::RenderProcessHostDestroyed "); 128 } 129 130 Shell* shell_; 131 std::string* logging_string_; 132 }; 133 134 class ObserverLogger : public RenderProcessHostObserver { 135 public: 136 explicit ObserverLogger(std::string* logging_string) 137 : logging_string_(logging_string), host_destroyed_(false) {} 138 139 bool host_destroyed() { return host_destroyed_; } 140 141 protected: 142 // RenderProcessHostObserver: 143 virtual void RenderProcessExited(RenderProcessHost* host, 144 base::ProcessHandle handle, 145 base::TerminationStatus status, 146 int exit_code) OVERRIDE { 147 logging_string_->append("ObserverLogger::RenderProcessExited "); 148 } 149 150 virtual void RenderProcessHostDestroyed(RenderProcessHost* host) OVERRIDE { 151 logging_string_->append("ObserverLogger::RenderProcessHostDestroyed "); 152 host_destroyed_ = true; 153 } 154 155 std::string* logging_string_; 156 bool host_destroyed_; 157 }; 158 159 IN_PROC_BROWSER_TEST_F(RenderProcessHostTest, 160 AllProcessExitedCallsBeforeAnyHostDestroyedCalls) { 161 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 162 163 GURL test_url = embedded_test_server()->GetURL("/simple_page.html"); 164 NavigateToURL(shell(), test_url); 165 166 std::string logging_string; 167 ShellCloser shell_closer(shell(), &logging_string); 168 ObserverLogger observer_logger(&logging_string); 169 RenderProcessHost* rph = 170 shell()->web_contents()->GetRenderViewHost()->GetProcess(); 171 172 // Ensure that the ShellCloser observer is first, so that it will have first 173 // dibs on the ProcessExited callback. 174 rph->AddObserver(&shell_closer); 175 rph->AddObserver(&observer_logger); 176 177 // This will crash the render process, and start all the callbacks. 178 NavigateToURL(shell(), GURL(kChromeUICrashURL)); 179 180 // The key here is that all the RenderProcessExited callbacks precede all the 181 // RenderProcessHostDestroyed callbacks. 182 EXPECT_EQ("ShellCloser::RenderProcessExited " 183 "ObserverLogger::RenderProcessExited " 184 "ShellCloser::RenderProcessHostDestroyed " 185 "ObserverLogger::RenderProcessHostDestroyed ", logging_string); 186 187 // If the test fails, and somehow the RPH is still alive somehow, at least 188 // deregister the observers so that the test fails and doesn't also crash. 189 if (!observer_logger.host_destroyed()) { 190 rph->RemoveObserver(&shell_closer); 191 rph->RemoveObserver(&observer_logger); 192 } 193 } 194 195 } // namespace 196 } // namespace content 197