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