render_process_host_chrome_browsertest.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
1// Copyright (c) 2012 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 "chrome/browser/debugger/devtools_window.h"
7#include "chrome/browser/ui/browser.h"
8#include "chrome/browser/ui/browser_commands.h"
9#include "chrome/browser/ui/browser_tabstrip.h"
10#include "chrome/browser/ui/singleton_tabs.h"
11#include "chrome/common/chrome_switches.h"
12#include "chrome/common/url_constants.h"
13#include "chrome/test/base/in_process_browser_test.h"
14#include "chrome/test/base/ui_test_utils.h"
15#include "content/public/browser/render_process_host.h"
16#include "content/public/browser/render_view_host.h"
17#include "content/public/browser/web_contents.h"
18
19using content::RenderViewHost;
20using content::RenderWidgetHost;
21using content::WebContents;
22
23namespace {
24
25int RenderProcessHostCount() {
26  content::RenderProcessHost::iterator hosts =
27      content::RenderProcessHost::AllHostsIterator();
28  int count = 0;
29  while (!hosts.IsAtEnd()) {
30    if (hosts.GetCurrentValue()->HasConnection())
31      count++;
32    hosts.Advance();
33  }
34  return count;
35}
36
37RenderViewHost* FindFirstDevToolsHost() {
38  content::RenderProcessHost::iterator hosts =
39      content::RenderProcessHost::AllHostsIterator();
40  for (; !hosts.IsAtEnd(); hosts.Advance()) {
41    content::RenderProcessHost* render_process_host = hosts.GetCurrentValue();
42    DCHECK(render_process_host);
43    if (!render_process_host->HasConnection())
44      continue;
45    content::RenderProcessHost::RenderWidgetHostsIterator iter(
46        render_process_host->GetRenderWidgetHostsIterator());
47    for (; !iter.IsAtEnd(); iter.Advance()) {
48      const RenderWidgetHost* widget = iter.GetCurrentValue();
49      DCHECK(widget);
50      if (!widget || !widget->IsRenderView())
51        continue;
52      RenderViewHost* host =
53          RenderViewHost::From(const_cast<RenderWidgetHost*>(widget));
54      WebContents* contents = WebContents::FromRenderViewHost(host);
55      GURL url = contents->GetURL();
56      if (url.SchemeIs(chrome::kChromeDevToolsScheme))
57        return host;
58    }
59  }
60  return NULL;
61}
62
63}  // namespace
64
65class ChromeRenderProcessHostTest : public InProcessBrowserTest {
66 public:
67  ChromeRenderProcessHostTest() {}
68
69  // Show a tab, activating the current one if there is one, and wait for
70  // the renderer process to be created or foregrounded, returning the process
71  // handle.
72  base::ProcessHandle ShowSingletonTab(const GURL& page) {
73    chrome::ShowSingletonTab(browser(), page);
74    WebContents* wc = chrome::GetActiveWebContents(browser());
75    CHECK(wc->GetURL() == page);
76
77    // Ensure that the backgrounding / foregrounding gets a chance to run.
78    content::BrowserThread::PostTaskAndReply(
79        content::BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
80        base::Bind(&base::DoNothing), MessageLoop::QuitClosure());
81    MessageLoop::current()->Run();
82
83    return wc->GetRenderProcessHost()->GetHandle();
84  }
85
86  // When we hit the max number of renderers, verify that the way we do process
87  // sharing behaves correctly.  In particular, this test is verifying that even
88  // when we hit the max process limit, that renderers of each type will wind up
89  // in a process of that type, even if that means creating a new process.
90  void TestProcessOverflow() {
91    int tab_count = 1;
92    int host_count = 1;
93    WebContents* tab1 = NULL;
94    WebContents* tab2 = NULL;
95    content::RenderProcessHost* rph1 = NULL;
96    content::RenderProcessHost* rph2 = NULL;
97    content::RenderProcessHost* rph3 = NULL;
98
99    // Change the first tab to be the new tab page (TYPE_WEBUI).
100    GURL newtab(chrome::kChromeUINewTabURL);
101    ui_test_utils::NavigateToURL(browser(), newtab);
102    EXPECT_EQ(tab_count, browser()->tab_count());
103    tab1 = chrome::GetWebContentsAt(browser(), tab_count - 1);
104    rph1 = tab1->GetRenderProcessHost();
105    EXPECT_EQ(tab1->GetURL(), newtab);
106    EXPECT_EQ(host_count, RenderProcessHostCount());
107
108    // Create a new TYPE_TABBED tab.  It should be in its own process.
109    GURL page1("data:text/html,hello world1");
110    chrome::ShowSingletonTab(browser(), page1);
111    if (browser()->tab_count() == tab_count)
112      ui_test_utils::WaitForNewTab(browser());
113    tab_count++;
114    host_count++;
115    EXPECT_EQ(tab_count, browser()->tab_count());
116    tab1 = chrome::GetWebContentsAt(browser(), tab_count - 1);
117    rph2 = tab1->GetRenderProcessHost();
118    EXPECT_EQ(tab1->GetURL(), page1);
119    EXPECT_EQ(host_count, RenderProcessHostCount());
120    EXPECT_NE(rph1, rph2);
121
122    // Create another TYPE_TABBED tab.  It should share the previous process.
123    GURL page2("data:text/html,hello world2");
124    chrome::ShowSingletonTab(browser(), page2);
125    if (browser()->tab_count() == tab_count)
126      ui_test_utils::WaitForNewTab(browser());
127    tab_count++;
128    EXPECT_EQ(tab_count, browser()->tab_count());
129    tab2 = chrome::GetWebContentsAt(browser(), tab_count - 1);
130    EXPECT_EQ(tab2->GetURL(), page2);
131    EXPECT_EQ(host_count, RenderProcessHostCount());
132    EXPECT_EQ(tab2->GetRenderProcessHost(), rph2);
133
134    // Create another TYPE_WEBUI tab.  It should share the process with newtab.
135    // Note: intentionally create this tab after the TYPE_TABBED tabs to
136    // exercise bug 43448 where extension and WebUI tabs could get combined into
137    // normal renderers.
138    GURL history(chrome::kChromeUIHistoryURL);
139    chrome::ShowSingletonTab(browser(), history);
140    if (browser()->tab_count() == tab_count)
141      ui_test_utils::WaitForNewTab(browser());
142    tab_count++;
143    EXPECT_EQ(tab_count, browser()->tab_count());
144    tab2 = chrome::GetWebContentsAt(browser(), tab_count - 1);
145    EXPECT_EQ(tab2->GetURL(), GURL(history));
146    EXPECT_EQ(host_count, RenderProcessHostCount());
147    EXPECT_EQ(tab2->GetRenderProcessHost(), rph1);
148
149    // Create a TYPE_EXTENSION tab.  It should be in its own process.
150    // (the bookmark manager is implemented as an extension)
151    GURL bookmarks(chrome::kChromeUIBookmarksURL);
152    chrome::ShowSingletonTab(browser(), bookmarks);
153    if (browser()->tab_count() == tab_count)
154      ui_test_utils::WaitForNewTab(browser());
155    tab_count++;
156    host_count++;
157    EXPECT_EQ(tab_count, browser()->tab_count());
158    tab1 = chrome::GetWebContentsAt(browser(), tab_count - 1);
159    rph3 = tab1->GetRenderProcessHost();
160    EXPECT_EQ(tab1->GetURL(), bookmarks);
161    EXPECT_EQ(host_count, RenderProcessHostCount());
162    EXPECT_NE(rph1, rph3);
163    EXPECT_NE(rph2, rph3);
164  }
165};
166
167
168class ChromeRenderProcessHostTestWithCommandLine
169    : public ChromeRenderProcessHostTest {
170 protected:
171  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
172    command_line->AppendSwitchASCII(switches::kRendererProcessLimit, "1");
173  }
174};
175
176IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, ProcessPerTab) {
177  // Set max renderers to 1 to force running out of processes.
178  content::RenderProcessHost::SetMaxRendererProcessCount(1);
179
180  CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
181  parsed_command_line.AppendSwitch(switches::kProcessPerTab);
182
183  int tab_count = 1;
184  int host_count = 1;
185
186  // Change the first tab to be the new tab page (TYPE_WEBUI).
187  GURL newtab(chrome::kChromeUINewTabURL);
188  ui_test_utils::NavigateToURL(browser(), newtab);
189  EXPECT_EQ(tab_count, browser()->tab_count());
190  EXPECT_EQ(host_count, RenderProcessHostCount());
191
192  // Create a new TYPE_TABBED tab.  It should be in its own process.
193  GURL page1("data:text/html,hello world1");
194  chrome::ShowSingletonTab(browser(), page1);
195  if (browser()->tab_count() == tab_count)
196    ui_test_utils::WaitForNewTab(browser());
197  tab_count++;
198  host_count++;
199  EXPECT_EQ(tab_count, browser()->tab_count());
200  EXPECT_EQ(host_count, RenderProcessHostCount());
201
202  // Create another TYPE_TABBED tab.  It should share the previous process.
203  GURL page2("data:text/html,hello world2");
204  chrome::ShowSingletonTab(browser(), page2);
205  if (browser()->tab_count() == tab_count)
206    ui_test_utils::WaitForNewTab(browser());
207  tab_count++;
208  EXPECT_EQ(tab_count, browser()->tab_count());
209  EXPECT_EQ(host_count, RenderProcessHostCount());
210
211  // Create another new tab.  It should share the process with the other WebUI.
212  chrome::NewTab(browser());
213  if (browser()->tab_count() == tab_count)
214    ui_test_utils::WaitForNewTab(browser());
215  tab_count++;
216  EXPECT_EQ(tab_count, browser()->tab_count());
217  EXPECT_EQ(host_count, RenderProcessHostCount());
218
219  // Create another new tab.  It should share the process with the other WebUI.
220  chrome::NewTab(browser());
221  if (browser()->tab_count() == tab_count)
222    ui_test_utils::WaitForNewTab(browser());
223  tab_count++;
224  EXPECT_EQ(tab_count, browser()->tab_count());
225  EXPECT_EQ(host_count, RenderProcessHostCount());
226}
227
228// We don't change process priorities on Mac or Posix because the user lacks the
229// permission to raise a process' priority even after lowering it.
230#if defined(OS_WIN) || defined(OS_LINUX)
231IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, Backgrounding) {
232  if (!base::Process::CanBackgroundProcesses()) {
233    LOG(ERROR) << "Can't background processes";
234    return;
235  }
236  CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
237  parsed_command_line.AppendSwitch(switches::kProcessPerTab);
238
239  // Change the first tab to be the new tab page (TYPE_WEBUI).
240  GURL newtab(chrome::kChromeUINewTabURL);
241  ui_test_utils::NavigateToURL(browser(), newtab);
242
243  // Create a new tab. It should be foreground.
244  GURL page1("data:text/html,hello world1");
245  base::ProcessHandle pid1 = ShowSingletonTab(page1);
246  EXPECT_FALSE(base::Process(pid1).IsProcessBackgrounded());
247
248  // Create another tab. It should be foreground, and the first tab should
249  // now be background.
250  GURL page2("data:text/html,hello world2");
251  base::ProcessHandle pid2 = ShowSingletonTab(page2);
252  EXPECT_NE(pid1, pid2);
253  EXPECT_TRUE(base::Process(pid1).IsProcessBackgrounded());
254  EXPECT_FALSE(base::Process(pid2).IsProcessBackgrounded());
255
256  // Navigate back to first page. It should be foreground again, and the second
257  // tab should be background.
258  EXPECT_EQ(pid1, ShowSingletonTab(page1));
259  EXPECT_FALSE(base::Process(pid1).IsProcessBackgrounded());
260  EXPECT_TRUE(base::Process(pid2).IsProcessBackgrounded());
261}
262#endif
263
264IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, ProcessOverflow) {
265  // Set max renderers to 1 to force running out of processes.
266  content::RenderProcessHost::SetMaxRendererProcessCount(1);
267  TestProcessOverflow();
268}
269
270// Variation of the ProcessOverflow test, which is driven through command line
271// parameter instead of direct function call into the class.
272IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTestWithCommandLine,
273                       ProcessOverflow) {
274  TestProcessOverflow();
275}
276
277// Ensure that DevTools opened to debug DevTools is launched in a separate
278// process when --process-per-tab is set. See crbug.com/69873.
279IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest,
280                       DevToolsOnSelfInOwnProcessPPT) {
281  CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
282  parsed_command_line.AppendSwitch(switches::kProcessPerTab);
283
284  int tab_count = 1;
285  int host_count = 1;
286
287  GURL page1("data:text/html,hello world1");
288  chrome::ShowSingletonTab(browser(), page1);
289  if (browser()->tab_count() == tab_count)
290    ui_test_utils::WaitForNewTab(browser());
291  tab_count++;
292  host_count++;
293  EXPECT_EQ(tab_count, browser()->tab_count());
294  EXPECT_EQ(host_count, RenderProcessHostCount());
295
296  // DevTools start in docked mode (no new tab), in a separate process.
297  chrome::ToggleDevToolsWindow(browser(), DEVTOOLS_TOGGLE_ACTION_INSPECT);
298  host_count++;
299  EXPECT_EQ(tab_count, browser()->tab_count());
300  EXPECT_EQ(host_count, RenderProcessHostCount());
301
302  RenderViewHost* devtools = FindFirstDevToolsHost();
303  DCHECK(devtools);
304
305  // DevTools start in a separate process.
306  DevToolsWindow::ToggleDevToolsWindow(
307      devtools, true, DEVTOOLS_TOGGLE_ACTION_INSPECT);
308  host_count++;
309  EXPECT_EQ(tab_count, browser()->tab_count());
310  EXPECT_EQ(host_count, RenderProcessHostCount());
311}
312
313// Ensure that DevTools opened to debug DevTools is launched in a separate
314// process. See crbug.com/69873.
315IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest,
316                       DevToolsOnSelfInOwnProcess) {
317  int tab_count = 1;
318  int host_count = 1;
319
320  GURL page1("data:text/html,hello world1");
321  chrome::ShowSingletonTab(browser(), page1);
322  if (browser()->tab_count() == tab_count)
323    ui_test_utils::WaitForNewTab(browser());
324  tab_count++;
325  host_count++;
326  EXPECT_EQ(tab_count, browser()->tab_count());
327  EXPECT_EQ(host_count, RenderProcessHostCount());
328
329  // DevTools start in docked mode (no new tab), in a separate process.
330  chrome::ToggleDevToolsWindow(browser(), DEVTOOLS_TOGGLE_ACTION_INSPECT);
331  host_count++;
332  EXPECT_EQ(tab_count, browser()->tab_count());
333  EXPECT_EQ(host_count, RenderProcessHostCount());
334
335  RenderViewHost* devtools = FindFirstDevToolsHost();
336  DCHECK(devtools);
337
338  // DevTools start in a separate process.
339  DevToolsWindow::ToggleDevToolsWindow(
340      devtools, true, DEVTOOLS_TOGGLE_ACTION_INSPECT);
341  host_count++;
342  EXPECT_EQ(tab_count, browser()->tab_count());
343  EXPECT_EQ(host_count, RenderProcessHostCount());
344}
345