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