Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / chrome / browser / renderer_host / render_process_host_chrome_browsertest.cc
blob17faa6406d94050ee4dd1baa7f110d2e33c4b838
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/chrome_notification_types.h"
7 #include "chrome/browser/devtools/devtools_window.h"
8 #include "chrome/browser/search/search.h"
9 #include "chrome/browser/ui/browser.h"
10 #include "chrome/browser/ui/browser_commands.h"
11 #include "chrome/browser/ui/singleton_tabs.h"
12 #include "chrome/browser/ui/tabs/tab_strip_model.h"
13 #include "chrome/common/chrome_switches.h"
14 #include "chrome/common/url_constants.h"
15 #include "chrome/test/base/in_process_browser_test.h"
16 #include "chrome/test/base/test_switches.h"
17 #include "chrome/test/base/ui_test_utils.h"
18 #include "content/public/browser/notification_service.h"
19 #include "content/public/browser/render_process_host.h"
20 #include "content/public/browser/render_view_host.h"
21 #include "content/public/browser/render_widget_host_iterator.h"
22 #include "content/public/browser/web_contents.h"
23 #include "content/public/browser/web_contents_observer.h"
24 #include "content/public/test/browser_test_utils.h"
26 using content::RenderViewHost;
27 using content::RenderWidgetHost;
28 using content::WebContents;
30 namespace {
32 int RenderProcessHostCount() {
33 content::RenderProcessHost::iterator hosts =
34 content::RenderProcessHost::AllHostsIterator();
35 int count = 0;
36 while (!hosts.IsAtEnd()) {
37 if (hosts.GetCurrentValue()->HasConnection())
38 count++;
39 hosts.Advance();
41 return count;
44 WebContents* FindFirstDevToolsContents() {
45 scoped_ptr<content::RenderWidgetHostIterator> widgets(
46 RenderWidgetHost::GetRenderWidgetHosts());
47 while (content::RenderWidgetHost* widget = widgets->GetNextHost()) {
48 if (!widget->GetProcess()->HasConnection())
49 continue;
50 if (!widget->IsRenderView())
51 continue;
52 RenderViewHost* host = RenderViewHost::From(widget);
53 WebContents* contents = WebContents::FromRenderViewHost(host);
54 GURL url = contents->GetURL();
55 if (url.SchemeIs(content::kChromeDevToolsScheme))
56 return contents;
58 return NULL;
61 } // namespace
63 class ChromeRenderProcessHostTest : public InProcessBrowserTest {
64 public:
65 ChromeRenderProcessHostTest() {}
67 // Show a tab, activating the current one if there is one, and wait for
68 // the renderer process to be created or foregrounded, returning the process
69 // handle.
70 base::ProcessHandle ShowSingletonTab(const GURL& page) {
71 chrome::ShowSingletonTab(browser(), page);
72 WebContents* wc = browser()->tab_strip_model()->GetActiveWebContents();
73 CHECK(wc->GetURL() == page);
75 WaitForLauncherThread();
76 WaitForMessageProcessing(wc);
77 return wc->GetRenderProcessHost()->GetHandle();
80 // Loads the given url in a new background tab and returns the handle of its
81 // renderer.
82 base::ProcessHandle OpenBackgroundTab(const GURL& page) {
83 ui_test_utils::NavigateToURLWithDisposition(browser(), page,
84 NEW_BACKGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
86 TabStripModel* tab_strip = browser()->tab_strip_model();
87 WebContents* wc = tab_strip->GetWebContentsAt(
88 tab_strip->active_index() + 1);
89 CHECK(wc->GetVisibleURL() == page);
91 WaitForLauncherThread();
92 WaitForMessageProcessing(wc);
93 return wc->GetRenderProcessHost()->GetHandle();
96 // Ensures that the backgrounding / foregrounding gets a chance to run.
97 void WaitForLauncherThread() {
98 content::BrowserThread::PostTaskAndReply(
99 content::BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
100 base::Bind(&base::DoNothing), base::MessageLoop::QuitClosure());
101 base::MessageLoop::current()->Run();
104 // Implicitly waits for the renderer process associated with the specified
105 // WebContents to process outstanding IPC messages by running some JavaScript
106 // and waiting for the result.
107 void WaitForMessageProcessing(WebContents* wc) {
108 bool result = false;
109 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
110 wc, "window.domAutomationController.send(true);", &result));
111 ASSERT_TRUE(result);
114 // When we hit the max number of renderers, verify that the way we do process
115 // sharing behaves correctly. In particular, this test is verifying that even
116 // when we hit the max process limit, that renderers of each type will wind up
117 // in a process of that type, even if that means creating a new process.
118 void TestProcessOverflow() {
119 int tab_count = 1;
120 int host_count = 1;
121 WebContents* tab1 = NULL;
122 WebContents* tab2 = NULL;
123 content::RenderProcessHost* rph1 = NULL;
124 content::RenderProcessHost* rph2 = NULL;
125 content::RenderProcessHost* rph3 = NULL;
127 // Change the first tab to be the omnibox page (TYPE_WEBUI).
128 GURL omnibox(chrome::kChromeUIOmniboxURL);
129 ui_test_utils::NavigateToURL(browser(), omnibox);
130 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
131 tab1 = browser()->tab_strip_model()->GetWebContentsAt(tab_count - 1);
132 rph1 = tab1->GetRenderProcessHost();
133 EXPECT_EQ(omnibox, tab1->GetURL());
134 EXPECT_EQ(host_count, RenderProcessHostCount());
136 // Create a new TYPE_TABBED tab. It should be in its own process.
137 GURL page1("data:text/html,hello world1");
139 ui_test_utils::WindowedTabAddedNotificationObserver observer1(
140 content::NotificationService::AllSources());
141 chrome::ShowSingletonTab(browser(), page1);
142 observer1.Wait();
144 tab_count++;
145 host_count++;
146 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
147 tab1 = browser()->tab_strip_model()->GetWebContentsAt(tab_count - 1);
148 rph2 = tab1->GetRenderProcessHost();
149 EXPECT_EQ(tab1->GetURL(), page1);
150 EXPECT_EQ(host_count, RenderProcessHostCount());
151 EXPECT_NE(rph1, rph2);
153 // Create another TYPE_TABBED tab. It should share the previous process.
154 GURL page2("data:text/html,hello world2");
155 ui_test_utils::WindowedTabAddedNotificationObserver observer2(
156 content::NotificationService::AllSources());
157 chrome::ShowSingletonTab(browser(), page2);
158 observer2.Wait();
159 tab_count++;
160 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
161 tab2 = browser()->tab_strip_model()->GetWebContentsAt(tab_count - 1);
162 EXPECT_EQ(tab2->GetURL(), page2);
163 EXPECT_EQ(host_count, RenderProcessHostCount());
164 EXPECT_EQ(tab2->GetRenderProcessHost(), rph2);
166 // Create another TYPE_WEBUI tab. It should share the process with omnibox.
167 // Note: intentionally create this tab after the TYPE_TABBED tabs to
168 // exercise bug 43448 where extension and WebUI tabs could get combined into
169 // normal renderers.
170 GURL history(chrome::kChromeUIHistoryURL);
171 ui_test_utils::WindowedTabAddedNotificationObserver observer3(
172 content::NotificationService::AllSources());
173 chrome::ShowSingletonTab(browser(), history);
174 observer3.Wait();
175 tab_count++;
176 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
177 tab2 = browser()->tab_strip_model()->GetWebContentsAt(tab_count - 1);
178 EXPECT_EQ(tab2->GetURL(), GURL(history));
179 EXPECT_EQ(host_count, RenderProcessHostCount());
180 EXPECT_EQ(tab2->GetRenderProcessHost(), rph1);
182 // Create a TYPE_EXTENSION tab. It should be in its own process.
183 // (the bookmark manager is implemented as an extension)
184 GURL bookmarks(chrome::kChromeUIBookmarksURL);
185 ui_test_utils::WindowedTabAddedNotificationObserver observer4(
186 content::NotificationService::AllSources());
187 chrome::ShowSingletonTab(browser(), bookmarks);
188 observer4.Wait();
189 tab_count++;
190 host_count++;
191 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
192 tab1 = browser()->tab_strip_model()->GetWebContentsAt(tab_count - 1);
193 rph3 = tab1->GetRenderProcessHost();
194 EXPECT_EQ(tab1->GetURL(), bookmarks);
195 EXPECT_EQ(host_count, RenderProcessHostCount());
196 EXPECT_NE(rph1, rph3);
197 EXPECT_NE(rph2, rph3);
202 class ChromeRenderProcessHostTestWithCommandLine
203 : public ChromeRenderProcessHostTest {
204 protected:
205 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
206 command_line->AppendSwitchASCII(switches::kRendererProcessLimit, "1");
210 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, ProcessPerTab) {
211 // Set max renderers to 1 to force running out of processes.
212 content::RenderProcessHost::SetMaxRendererProcessCount(1);
214 CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
215 parsed_command_line.AppendSwitch(switches::kProcessPerTab);
217 int tab_count = 1;
218 int host_count = 1;
220 // Change the first tab to be the new tab page (TYPE_WEBUI).
221 GURL omnibox(chrome::kChromeUIOmniboxURL);
222 ui_test_utils::NavigateToURL(browser(), omnibox);
223 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
224 EXPECT_EQ(host_count, RenderProcessHostCount());
226 // Create a new TYPE_TABBED tab. It should be in its own process.
227 GURL page1("data:text/html,hello world1");
228 ui_test_utils::WindowedTabAddedNotificationObserver observer1(
229 content::NotificationService::AllSources());
230 chrome::ShowSingletonTab(browser(), page1);
231 observer1.Wait();
232 tab_count++;
233 host_count++;
234 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
235 EXPECT_EQ(host_count, RenderProcessHostCount());
237 // Create another TYPE_TABBED tab. It should share the previous process.
238 GURL page2("data:text/html,hello world2");
239 ui_test_utils::WindowedTabAddedNotificationObserver observer2(
240 content::NotificationService::AllSources());
241 chrome::ShowSingletonTab(browser(), page2);
242 observer2.Wait();
243 tab_count++;
244 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
245 EXPECT_EQ(host_count, RenderProcessHostCount());
247 // Create another omnibox tab. It should share the process with the other
248 // WebUI.
249 ui_test_utils::NavigateToURLWithDisposition(
250 browser(), omnibox, NEW_FOREGROUND_TAB,
251 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
252 tab_count++;
253 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
254 EXPECT_EQ(host_count, RenderProcessHostCount());
256 // Create another omnibox tab. It should share the process with the other
257 // WebUI.
258 ui_test_utils::NavigateToURLWithDisposition(
259 browser(), omnibox, NEW_FOREGROUND_TAB,
260 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
261 tab_count++;
262 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
263 EXPECT_EQ(host_count, RenderProcessHostCount());
266 // We don't change process priorities on Mac or Posix because the user lacks the
267 // permission to raise a process' priority even after lowering it.
268 #if defined(OS_WIN) || defined(OS_LINUX)
269 #if defined(OS_WIN)
270 // Flaky test: crbug.com/394368
271 #define MAYBE_Backgrounding DISABLED_Backgrounding
272 #else
273 #define MAYBE_Backgrounding Backgrounding
274 #endif
275 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, MAYBE_Backgrounding) {
276 if (!base::Process::CanBackgroundProcesses()) {
277 LOG(ERROR) << "Can't background processes";
278 return;
280 CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
281 parsed_command_line.AppendSwitch(switches::kProcessPerTab);
283 // Change the first tab to be the omnibox page (TYPE_WEBUI).
284 GURL omnibox(chrome::kChromeUIOmniboxURL);
285 ui_test_utils::NavigateToURL(browser(), omnibox);
287 // Create a new tab. It should be foreground.
288 GURL page1("data:text/html,hello world1");
289 base::ProcessHandle pid1 = ShowSingletonTab(page1);
290 EXPECT_FALSE(base::Process(pid1).IsProcessBackgrounded());
292 // Create another tab. It should be foreground, and the first tab should
293 // now be background.
294 GURL page2("data:text/html,hello world2");
295 base::ProcessHandle pid2 = ShowSingletonTab(page2);
296 EXPECT_NE(pid1, pid2);
297 EXPECT_TRUE(base::Process(pid1).IsProcessBackgrounded());
298 EXPECT_FALSE(base::Process(pid2).IsProcessBackgrounded());
300 // Load another tab in background. The renderer of the new tab should be
301 // backgrounded, while visibility of the other renderers should not change.
302 GURL page3("data:text/html,hello world3");
303 base::ProcessHandle pid3 = OpenBackgroundTab(page3);
304 EXPECT_NE(pid3, pid1);
305 EXPECT_NE(pid3, pid2);
306 EXPECT_TRUE(base::Process(pid1).IsProcessBackgrounded());
307 EXPECT_FALSE(base::Process(pid2).IsProcessBackgrounded());
308 EXPECT_TRUE(base::Process(pid3).IsProcessBackgrounded());
310 // Navigate back to the first page. Its renderer should be in foreground
311 // again while the other renderers should be backgrounded.
312 EXPECT_EQ(pid1, ShowSingletonTab(page1));
313 EXPECT_FALSE(base::Process(pid1).IsProcessBackgrounded());
314 EXPECT_TRUE(base::Process(pid2).IsProcessBackgrounded());
315 EXPECT_TRUE(base::Process(pid3).IsProcessBackgrounded());
317 #endif
319 // TODO(nasko): crbug.com/173137
320 #if defined(OS_WIN)
321 #define MAYBE_ProcessOverflow DISABLED_ProcessOverflow
322 #else
323 #define MAYBE_ProcessOverflow ProcessOverflow
324 #endif
326 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, MAYBE_ProcessOverflow) {
327 // Set max renderers to 1 to force running out of processes.
328 content::RenderProcessHost::SetMaxRendererProcessCount(1);
329 TestProcessOverflow();
332 // Variation of the ProcessOverflow test, which is driven through command line
333 // parameter instead of direct function call into the class.
334 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTestWithCommandLine,
335 ProcessOverflow) {
336 TestProcessOverflow();
339 // Ensure that DevTools opened to debug DevTools is launched in a separate
340 // process when --process-per-tab is set. See crbug.com/69873.
341 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest,
342 DevToolsOnSelfInOwnProcessPPT) {
343 #if defined(OS_WIN) && defined(USE_ASH)
344 // Disable this test in Metro+Ash for now (http://crbug.com/262796).
345 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
346 return;
347 #endif
349 CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
350 parsed_command_line.AppendSwitch(switches::kProcessPerTab);
352 int tab_count = 1;
353 int host_count = 1;
355 GURL page1("data:text/html,hello world1");
356 ui_test_utils::WindowedTabAddedNotificationObserver observer1(
357 content::NotificationService::AllSources());
358 chrome::ShowSingletonTab(browser(), page1);
359 observer1.Wait();
360 tab_count++;
361 host_count++;
362 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
363 EXPECT_EQ(host_count, RenderProcessHostCount());
365 // DevTools start in docked mode (no new tab), in a separate process.
366 chrome::ToggleDevToolsWindow(browser(), DevToolsToggleAction::Inspect());
367 host_count++;
368 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
369 EXPECT_EQ(host_count, RenderProcessHostCount());
371 WebContents* devtools = FindFirstDevToolsContents();
372 DCHECK(devtools);
374 // DevTools start in a separate process.
375 DevToolsWindow::OpenDevToolsWindow(devtools, DevToolsToggleAction::Inspect());
376 host_count++;
377 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
378 EXPECT_EQ(host_count, RenderProcessHostCount());
380 // close docked devtools
381 content::WindowedNotificationObserver close_observer(
382 content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
383 content::Source<WebContents>(devtools));
385 chrome::ToggleDevToolsWindow(browser(), DevToolsToggleAction::Toggle());
386 close_observer.Wait();
389 // Ensure that DevTools opened to debug DevTools is launched in a separate
390 // process. See crbug.com/69873.
391 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest,
392 DevToolsOnSelfInOwnProcess) {
393 #if defined(OS_WIN) && defined(USE_ASH)
394 // Disable this test in Metro+Ash for now (http://crbug.com/262796).
395 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
396 return;
397 #endif
399 int tab_count = 1;
400 int host_count = 1;
402 GURL page1("data:text/html,hello world1");
403 ui_test_utils::WindowedTabAddedNotificationObserver observer1(
404 content::NotificationService::AllSources());
405 chrome::ShowSingletonTab(browser(), page1);
406 observer1.Wait();
407 tab_count++;
408 host_count++;
409 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
410 EXPECT_EQ(host_count, RenderProcessHostCount());
412 // DevTools start in docked mode (no new tab), in a separate process.
413 chrome::ToggleDevToolsWindow(browser(), DevToolsToggleAction::Inspect());
414 host_count++;
415 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
416 EXPECT_EQ(host_count, RenderProcessHostCount());
418 WebContents* devtools = FindFirstDevToolsContents();
419 DCHECK(devtools);
421 // DevTools start in a separate process.
422 DevToolsWindow::OpenDevToolsWindow(devtools, DevToolsToggleAction::Inspect());
423 host_count++;
424 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
425 EXPECT_EQ(host_count, RenderProcessHostCount());
427 // close docked devtools
428 content::WindowedNotificationObserver close_observer(
429 content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
430 content::Source<content::WebContents>(devtools));
431 chrome::ToggleDevToolsWindow(browser(), DevToolsToggleAction::Toggle());
432 close_observer.Wait();
435 // This class's goal is to close the browser window when a renderer process has
436 // crashed. It does so by monitoring WebContents for RenderProcessGone event and
437 // closing the passed in TabStripModel. This is used in the following test case.
438 class WindowDestroyer : public content::WebContentsObserver {
439 public:
440 WindowDestroyer(content::WebContents* web_contents, TabStripModel* model)
441 : content::WebContentsObserver(web_contents),
442 tab_strip_model_(model) {
445 virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE {
446 // Wait for the window to be destroyed, which will ensure all other
447 // RenderViewHost objects are deleted before we return and proceed with
448 // the next iteration of notifications.
449 content::WindowedNotificationObserver observer(
450 chrome::NOTIFICATION_BROWSER_CLOSED,
451 content::NotificationService::AllSources());
452 tab_strip_model_->CloseAllTabs();
453 observer.Wait();
456 private:
457 TabStripModel* tab_strip_model_;
459 DISALLOW_COPY_AND_ASSIGN(WindowDestroyer);
462 // Test to ensure that while iterating through all listeners in
463 // RenderProcessHost and invalidating them, we remove them properly and don't
464 // access already freed objects. See http://crbug.com/255524.
465 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest,
466 CloseAllTabsDuringProcessDied) {
467 GURL url(chrome::kChromeUIOmniboxURL);
469 ui_test_utils::NavigateToURL(browser(), url);
470 ui_test_utils::NavigateToURLWithDisposition(
471 browser(), url, NEW_BACKGROUND_TAB,
472 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
474 EXPECT_EQ(2, browser()->tab_strip_model()->count());
476 WebContents* wc1 = browser()->tab_strip_model()->GetWebContentsAt(0);
477 WebContents* wc2 = browser()->tab_strip_model()->GetWebContentsAt(1);
478 EXPECT_EQ(wc1->GetRenderProcessHost(), wc2->GetRenderProcessHost());
480 // Create an object that will close the window on a process crash.
481 WindowDestroyer destroyer(wc1, browser()->tab_strip_model());
483 // Use NOTIFICATION_BROWSER_CLOSED instead of NOTIFICATION_WINDOW_CLOSED,
484 // since the latter is not implemented on OSX and the test will timeout,
485 // causing it to fail.
486 content::WindowedNotificationObserver observer(
487 chrome::NOTIFICATION_BROWSER_CLOSED,
488 content::NotificationService::AllSources());
490 // Kill the renderer process, simulating a crash. This should the ProcessDied
491 // method to be called. Alternatively, RenderProcessHost::OnChannelError can
492 // be called to directly force a call to ProcessDied.
493 base::KillProcess(wc1->GetRenderProcessHost()->GetHandle(), -1, true);
495 observer.Wait();