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