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.
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"
20 using content::RenderViewHost
;
21 using content::RenderWidgetHost
;
22 using content::WebContents
;
26 int RenderProcessHostCount() {
27 content::RenderProcessHost::iterator hosts
=
28 content::RenderProcessHost::AllHostsIterator();
30 while (!hosts
.IsAtEnd()) {
31 if (hosts
.GetCurrentValue()->HasConnection())
38 RenderViewHost
* 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())
46 content::RenderProcessHost::RenderWidgetHostsIterator
iter(
47 render_process_host
->GetRenderWidgetHostsIterator());
48 for (; !iter
.IsAtEnd(); iter
.Advance()) {
49 const RenderWidgetHost
* widget
= iter
.GetCurrentValue();
51 if (!widget
|| !widget
->IsRenderView())
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
))
66 class ChromeRenderProcessHostTest
: public InProcessBrowserTest
{
68 ChromeRenderProcessHostTest() {}
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
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
);
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
), MessageLoop::QuitClosure());
82 MessageLoop::current()->Run();
84 return wc
->GetRenderProcessHost()->GetHandle();
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() {
94 WebContents
* tab1
= NULL
;
95 WebContents
* tab2
= NULL
;
96 content::RenderProcessHost
* rph1
= NULL
;
97 content::RenderProcessHost
* rph2
= NULL
;
98 content::RenderProcessHost
* rph3
= NULL
;
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());
109 // Create a new TYPE_TABBED tab. It should be in its own process.
110 GURL
page1("data:text/html,hello world1");
112 ui_test_utils::WindowedTabAddedNotificationObserver
observer1(
113 content::NotificationService::AllSources());
114 chrome::ShowSingletonTab(browser(), page1
);
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
);
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
);
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
);
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
143 GURL
history(chrome::kChromeUIHistoryURL
);
144 ui_test_utils::WindowedTabAddedNotificationObserver
observer3(
145 content::NotificationService::AllSources());
146 chrome::ShowSingletonTab(browser(), history
);
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
);
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
);
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
);
175 class ChromeRenderProcessHostTestWithCommandLine
176 : public ChromeRenderProcessHostTest
{
178 virtual void SetUpCommandLine(CommandLine
* command_line
) OVERRIDE
{
179 command_line
->AppendSwitchASCII(switches::kRendererProcessLimit
, "1");
183 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest
, ProcessPerTab
) {
184 // Set max renderers to 1 to force running out of processes.
185 content::RenderProcessHost::SetMaxRendererProcessCount(1);
187 CommandLine
& parsed_command_line
= *CommandLine::ForCurrentProcess();
188 parsed_command_line
.AppendSwitch(switches::kProcessPerTab
);
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());
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
);
207 EXPECT_EQ(tab_count
, browser()->tab_strip_model()->count());
208 EXPECT_EQ(host_count
, RenderProcessHostCount());
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
);
217 EXPECT_EQ(tab_count
, browser()->tab_strip_model()->count());
218 EXPECT_EQ(host_count
, RenderProcessHostCount());
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());
226 EXPECT_EQ(tab_count
, browser()->tab_strip_model()->count());
227 EXPECT_EQ(host_count
, RenderProcessHostCount());
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());
235 EXPECT_EQ(tab_count
, browser()->tab_strip_model()->count());
236 EXPECT_EQ(host_count
, RenderProcessHostCount());
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)
242 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest
, Backgrounding
) {
243 if (!base::Process::CanBackgroundProcesses()) {
244 LOG(ERROR
) << "Can't background processes";
247 CommandLine
& parsed_command_line
= *CommandLine::ForCurrentProcess();
248 parsed_command_line
.AppendSwitch(switches::kProcessPerTab
);
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
);
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());
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());
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());
275 // TODO(nasko): crbug.com/173137
277 #define MAYBE_ProcessOverflow DISABLED_ProcessOverflow
279 #define MAYBE_ProcessOverflow ProcessOverflow
282 IN_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();
288 // Variation of the ProcessOverflow test, which is driven through command line
289 // parameter instead of direct function call into the class.
290 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTestWithCommandLine
,
292 TestProcessOverflow();
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.
297 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest
,
298 DevToolsOnSelfInOwnProcessPPT
) {
299 CommandLine
& parsed_command_line
= *CommandLine::ForCurrentProcess();
300 parsed_command_line
.AppendSwitch(switches::kProcessPerTab
);
305 GURL
page1("data:text/html,hello world1");
306 ui_test_utils::WindowedTabAddedNotificationObserver
observer1(
307 content::NotificationService::AllSources());
308 chrome::ShowSingletonTab(browser(), page1
);
312 EXPECT_EQ(tab_count
, browser()->tab_strip_model()->count());
313 EXPECT_EQ(host_count
, RenderProcessHostCount());
315 // DevTools start in docked mode (no new tab), in a separate process.
316 chrome::ToggleDevToolsWindow(browser(), DEVTOOLS_TOGGLE_ACTION_INSPECT
);
318 EXPECT_EQ(tab_count
, browser()->tab_strip_model()->count());
319 EXPECT_EQ(host_count
, RenderProcessHostCount());
321 RenderViewHost
* devtools
= FindFirstDevToolsHost();
324 // DevTools start in a separate process.
325 DevToolsWindow::ToggleDevToolsWindow(
326 devtools
, true, DEVTOOLS_TOGGLE_ACTION_INSPECT
);
328 EXPECT_EQ(tab_count
, browser()->tab_strip_model()->count());
329 EXPECT_EQ(host_count
, RenderProcessHostCount());
332 // Ensure that DevTools opened to debug DevTools is launched in a separate
333 // process. See crbug.com/69873.
334 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest
,
335 DevToolsOnSelfInOwnProcess
) {
339 GURL
page1("data:text/html,hello world1");
340 ui_test_utils::WindowedTabAddedNotificationObserver
observer1(
341 content::NotificationService::AllSources());
342 chrome::ShowSingletonTab(browser(), page1
);
346 EXPECT_EQ(tab_count
, browser()->tab_strip_model()->count());
347 EXPECT_EQ(host_count
, RenderProcessHostCount());
349 // DevTools start in docked mode (no new tab), in a separate process.
350 chrome::ToggleDevToolsWindow(browser(), DEVTOOLS_TOGGLE_ACTION_INSPECT
);
352 EXPECT_EQ(tab_count
, browser()->tab_strip_model()->count());
353 EXPECT_EQ(host_count
, RenderProcessHostCount());
355 RenderViewHost
* devtools
= FindFirstDevToolsHost();
358 // DevTools start in a separate process.
359 DevToolsWindow::ToggleDevToolsWindow(
360 devtools
, true, DEVTOOLS_TOGGLE_ACTION_INSPECT
);
362 EXPECT_EQ(tab_count
, browser()->tab_strip_model()->count());
363 EXPECT_EQ(host_count
, RenderProcessHostCount());