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