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