Changes for start_with_ext Telemetry test to work on Windows.
[chromium-blink-merge.git] / chrome / browser / renderer_host / render_process_host_chrome_browsertest.cc
blob24c58ef9a6acd950d566e73f5f62e0f5225ad8ed
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 "base/process/process.h"
7 #include "chrome/browser/chrome_notification_types.h"
8 #include "chrome/browser/devtools/devtools_window.h"
9 #include "chrome/browser/search/search.h"
10 #include "chrome/browser/ui/browser.h"
11 #include "chrome/browser/ui/browser_commands.h"
12 #include "chrome/browser/ui/singleton_tabs.h"
13 #include "chrome/browser/ui/tabs/tab_strip_model.h"
14 #include "chrome/common/chrome_switches.h"
15 #include "chrome/common/url_constants.h"
16 #include "chrome/test/base/in_process_browser_test.h"
17 #include "chrome/test/base/test_switches.h"
18 #include "chrome/test/base/ui_test_utils.h"
19 #include "content/public/browser/notification_service.h"
20 #include "content/public/browser/render_process_host.h"
21 #include "content/public/browser/render_view_host.h"
22 #include "content/public/browser/render_widget_host_iterator.h"
23 #include "content/public/browser/web_contents.h"
24 #include "content/public/browser/web_contents_observer.h"
25 #include "content/public/test/browser_test_utils.h"
27 using content::RenderViewHost;
28 using content::RenderWidgetHost;
29 using content::WebContents;
31 namespace {
33 int RenderProcessHostCount() {
34 content::RenderProcessHost::iterator hosts =
35 content::RenderProcessHost::AllHostsIterator();
36 int count = 0;
37 while (!hosts.IsAtEnd()) {
38 if (hosts.GetCurrentValue()->HasConnection())
39 count++;
40 hosts.Advance();
42 return count;
45 WebContents* FindFirstDevToolsContents() {
46 scoped_ptr<content::RenderWidgetHostIterator> widgets(
47 RenderWidgetHost::GetRenderWidgetHosts());
48 while (content::RenderWidgetHost* widget = widgets->GetNextHost()) {
49 if (!widget->GetProcess()->HasConnection())
50 continue;
51 if (!widget->IsRenderView())
52 continue;
53 RenderViewHost* host = RenderViewHost::From(widget);
54 WebContents* contents = WebContents::FromRenderViewHost(host);
55 GURL url = contents->GetURL();
56 if (url.SchemeIs(content::kChromeDevToolsScheme))
57 return contents;
59 return NULL;
62 // TODO(rvargas) crbug.com/417532: Remove this code.
63 base::Process ProcessFromHandle(base::ProcessHandle handle) {
64 #if defined(OS_WIN)
65 if (handle == GetCurrentProcess())
66 return base::Process::Current();
68 base::ProcessHandle out_handle;
69 if (!::DuplicateHandle(GetCurrentProcess(), handle, GetCurrentProcess(),
70 &out_handle, 0, FALSE, DUPLICATE_SAME_ACCESS)) {
71 return base::Process();
73 handle = out_handle;
74 #endif // defined(OS_WIN)
75 return base::Process(handle);
78 } // namespace
80 class ChromeRenderProcessHostTest : public InProcessBrowserTest {
81 public:
82 ChromeRenderProcessHostTest() {}
84 // Show a tab, activating the current one if there is one, and wait for
85 // the renderer process to be created or foregrounded, returning the process
86 // handle.
87 base::Process ShowSingletonTab(const GURL& page) {
88 chrome::ShowSingletonTab(browser(), page);
89 WebContents* wc = browser()->tab_strip_model()->GetActiveWebContents();
90 CHECK(wc->GetURL() == page);
92 WaitForLauncherThread();
93 WaitForMessageProcessing(wc);
94 return ProcessFromHandle(wc->GetRenderProcessHost()->GetHandle());
97 // Loads the given url in a new background tab and returns the handle of its
98 // renderer.
99 base::Process OpenBackgroundTab(const GURL& page) {
100 ui_test_utils::NavigateToURLWithDisposition(browser(), page,
101 NEW_BACKGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
103 TabStripModel* tab_strip = browser()->tab_strip_model();
104 WebContents* wc = tab_strip->GetWebContentsAt(
105 tab_strip->active_index() + 1);
106 CHECK(wc->GetVisibleURL() == page);
108 WaitForLauncherThread();
109 WaitForMessageProcessing(wc);
110 return ProcessFromHandle(wc->GetRenderProcessHost()->GetHandle());
113 // Ensures that the backgrounding / foregrounding gets a chance to run.
114 void WaitForLauncherThread() {
115 content::BrowserThread::PostTaskAndReply(
116 content::BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
117 base::Bind(&base::DoNothing), base::MessageLoop::QuitClosure());
118 base::MessageLoop::current()->Run();
121 // Implicitly waits for the renderer process associated with the specified
122 // WebContents to process outstanding IPC messages by running some JavaScript
123 // and waiting for the result.
124 void WaitForMessageProcessing(WebContents* wc) {
125 bool result = false;
126 ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
127 wc, "window.domAutomationController.send(true);", &result));
128 ASSERT_TRUE(result);
131 // When we hit the max number of renderers, verify that the way we do process
132 // sharing behaves correctly. In particular, this test is verifying that even
133 // when we hit the max process limit, that renderers of each type will wind up
134 // in a process of that type, even if that means creating a new process.
135 void TestProcessOverflow() {
136 int tab_count = 1;
137 int host_count = 1;
138 WebContents* tab1 = NULL;
139 WebContents* tab2 = NULL;
140 content::RenderProcessHost* rph1 = NULL;
141 content::RenderProcessHost* rph2 = NULL;
142 content::RenderProcessHost* rph3 = NULL;
144 // Change the first tab to be the omnibox page (TYPE_WEBUI).
145 GURL omnibox(chrome::kChromeUIOmniboxURL);
146 ui_test_utils::NavigateToURL(browser(), omnibox);
147 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
148 tab1 = browser()->tab_strip_model()->GetWebContentsAt(tab_count - 1);
149 rph1 = tab1->GetRenderProcessHost();
150 EXPECT_EQ(omnibox, tab1->GetURL());
151 EXPECT_EQ(host_count, RenderProcessHostCount());
153 // Create a new TYPE_TABBED tab. It should be in its own process.
154 GURL page1("data:text/html,hello world1");
156 ui_test_utils::WindowedTabAddedNotificationObserver observer1(
157 content::NotificationService::AllSources());
158 chrome::ShowSingletonTab(browser(), page1);
159 observer1.Wait();
161 tab_count++;
162 host_count++;
163 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
164 tab1 = browser()->tab_strip_model()->GetWebContentsAt(tab_count - 1);
165 rph2 = tab1->GetRenderProcessHost();
166 EXPECT_EQ(tab1->GetURL(), page1);
167 EXPECT_EQ(host_count, RenderProcessHostCount());
168 EXPECT_NE(rph1, rph2);
170 // Create another TYPE_TABBED tab. It should share the previous process.
171 GURL page2("data:text/html,hello world2");
172 ui_test_utils::WindowedTabAddedNotificationObserver observer2(
173 content::NotificationService::AllSources());
174 chrome::ShowSingletonTab(browser(), page2);
175 observer2.Wait();
176 tab_count++;
177 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
178 tab2 = browser()->tab_strip_model()->GetWebContentsAt(tab_count - 1);
179 EXPECT_EQ(tab2->GetURL(), page2);
180 EXPECT_EQ(host_count, RenderProcessHostCount());
181 EXPECT_EQ(tab2->GetRenderProcessHost(), rph2);
183 // Create another TYPE_WEBUI tab. It should share the process with omnibox.
184 // Note: intentionally create this tab after the TYPE_TABBED tabs to
185 // exercise bug 43448 where extension and WebUI tabs could get combined into
186 // normal renderers.
187 GURL history(chrome::kChromeUIHistoryURL);
188 ui_test_utils::WindowedTabAddedNotificationObserver observer3(
189 content::NotificationService::AllSources());
190 chrome::ShowSingletonTab(browser(), history);
191 observer3.Wait();
192 tab_count++;
193 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
194 tab2 = browser()->tab_strip_model()->GetWebContentsAt(tab_count - 1);
195 EXPECT_EQ(tab2->GetURL(), GURL(history));
196 EXPECT_EQ(host_count, RenderProcessHostCount());
197 EXPECT_EQ(tab2->GetRenderProcessHost(), rph1);
199 // Create a TYPE_EXTENSION tab. It should be in its own process.
200 // (the bookmark manager is implemented as an extension)
201 GURL bookmarks(chrome::kChromeUIBookmarksURL);
202 ui_test_utils::WindowedTabAddedNotificationObserver observer4(
203 content::NotificationService::AllSources());
204 chrome::ShowSingletonTab(browser(), bookmarks);
205 observer4.Wait();
206 tab_count++;
207 host_count++;
208 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
209 tab1 = browser()->tab_strip_model()->GetWebContentsAt(tab_count - 1);
210 rph3 = tab1->GetRenderProcessHost();
211 EXPECT_EQ(tab1->GetURL(), bookmarks);
212 EXPECT_EQ(host_count, RenderProcessHostCount());
213 EXPECT_NE(rph1, rph3);
214 EXPECT_NE(rph2, rph3);
219 class ChromeRenderProcessHostTestWithCommandLine
220 : public ChromeRenderProcessHostTest {
221 protected:
222 void SetUpCommandLine(base::CommandLine* command_line) override {
223 command_line->AppendSwitchASCII(switches::kRendererProcessLimit, "1");
227 // Disable on Mac and Windows due to ongoing flakiness. (crbug.com/442785)
228 #if defined(OS_MACOSX) || defined(OS_WIN)
229 #define MAYBE_ProcessPerTab DISABLED_ProcessPerTab
230 #else
231 #define MAYBE_ProcessPerTab ProcessPerTab
232 #endif
234 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, MAYBE_ProcessPerTab) {
235 // Set max renderers to 1 to force running out of processes.
236 content::RenderProcessHost::SetMaxRendererProcessCount(1);
238 base::CommandLine& parsed_command_line =
239 *base::CommandLine::ForCurrentProcess();
240 parsed_command_line.AppendSwitch(switches::kProcessPerTab);
242 int tab_count = 1;
243 int host_count = 1;
245 // Change the first tab to be the new tab page (TYPE_WEBUI).
246 GURL omnibox(chrome::kChromeUIOmniboxURL);
247 ui_test_utils::NavigateToURL(browser(), omnibox);
248 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
249 EXPECT_EQ(host_count, RenderProcessHostCount());
251 // Create a new TYPE_TABBED tab. It should be in its own process.
252 GURL page1("data:text/html,hello world1");
253 ui_test_utils::WindowedTabAddedNotificationObserver observer1(
254 content::NotificationService::AllSources());
255 chrome::ShowSingletonTab(browser(), page1);
256 observer1.Wait();
257 tab_count++;
258 host_count++;
259 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
260 EXPECT_EQ(host_count, RenderProcessHostCount());
262 // Create another TYPE_TABBED tab. It should share the previous process.
263 GURL page2("data:text/html,hello world2");
264 ui_test_utils::WindowedTabAddedNotificationObserver observer2(
265 content::NotificationService::AllSources());
266 chrome::ShowSingletonTab(browser(), page2);
267 observer2.Wait();
268 tab_count++;
269 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
270 EXPECT_EQ(host_count, RenderProcessHostCount());
272 // Create another omnibox tab. It should share the process with the other
273 // WebUI.
274 ui_test_utils::NavigateToURLWithDisposition(
275 browser(), omnibox, NEW_FOREGROUND_TAB,
276 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
277 tab_count++;
278 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
279 EXPECT_EQ(host_count, RenderProcessHostCount());
281 // Create another omnibox tab. It should share the process with the other
282 // WebUI.
283 ui_test_utils::NavigateToURLWithDisposition(
284 browser(), omnibox, NEW_FOREGROUND_TAB,
285 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
286 tab_count++;
287 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
288 EXPECT_EQ(host_count, RenderProcessHostCount());
291 // We don't change process priorities on Mac or Posix because the user lacks the
292 // permission to raise a process' priority even after lowering it.
293 #if defined(OS_WIN) || defined(OS_LINUX)
294 #if defined(OS_WIN)
295 // Flaky test: crbug.com/394368
296 #define MAYBE_Backgrounding DISABLED_Backgrounding
297 #else
298 #define MAYBE_Backgrounding Backgrounding
299 #endif
300 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, MAYBE_Backgrounding) {
301 if (!base::Process::CanBackgroundProcesses()) {
302 LOG(ERROR) << "Can't background processes";
303 return;
305 base::CommandLine& parsed_command_line =
306 *base::CommandLine::ForCurrentProcess();
307 parsed_command_line.AppendSwitch(switches::kProcessPerTab);
309 // Change the first tab to be the omnibox page (TYPE_WEBUI).
310 GURL omnibox(chrome::kChromeUIOmniboxURL);
311 ui_test_utils::NavigateToURL(browser(), omnibox);
313 // Create a new tab. It should be foreground.
314 GURL page1("data:text/html,hello world1");
315 base::Process process1 = ShowSingletonTab(page1);
316 ASSERT_TRUE(process1.IsValid());
317 EXPECT_FALSE(process1.IsProcessBackgrounded());
319 // Create another tab. It should be foreground, and the first tab should
320 // now be background.
321 GURL page2("data:text/html,hello world2");
322 base::Process process2 = ShowSingletonTab(page2);
323 ASSERT_TRUE(process2.IsValid());
324 EXPECT_NE(process1.Pid(), process2.Pid());
325 EXPECT_TRUE(process1.IsProcessBackgrounded());
326 EXPECT_FALSE(process2.IsProcessBackgrounded());
328 // Load another tab in background. The renderer of the new tab should be
329 // backgrounded, while visibility of the other renderers should not change.
330 GURL page3("data:text/html,hello world3");
331 base::Process process3 = OpenBackgroundTab(page3);
332 ASSERT_TRUE(process3.IsValid());
333 EXPECT_NE(process3.Pid(), process1.Pid());
334 EXPECT_NE(process3.Pid(), process2.Pid());
335 EXPECT_TRUE(process1.IsProcessBackgrounded());
336 EXPECT_FALSE(process2.IsProcessBackgrounded());
337 EXPECT_TRUE(process3.IsProcessBackgrounded());
339 // Navigate back to the first page. Its renderer should be in foreground
340 // again while the other renderers should be backgrounded.
341 EXPECT_EQ(process1.Pid(), ShowSingletonTab(page1).Pid());
342 EXPECT_FALSE(process1.IsProcessBackgrounded());
343 EXPECT_TRUE(process2.IsProcessBackgrounded());
344 EXPECT_TRUE(process3.IsProcessBackgrounded());
346 #endif
348 // TODO(nasko): crbug.com/173137
349 // Disable on Mac 10.9 due to ongoing flakiness. (crbug.com/442785)
350 #if defined(OS_WIN) || defined(OS_MACOSX)
351 #define MAYBE_ProcessOverflow DISABLED_ProcessOverflow
352 #else
353 #define MAYBE_ProcessOverflow ProcessOverflow
354 #endif
356 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, MAYBE_ProcessOverflow) {
357 // Set max renderers to 1 to force running out of processes.
358 content::RenderProcessHost::SetMaxRendererProcessCount(1);
359 TestProcessOverflow();
362 // Disable on Mac 10.9 due to ongoing flakiness. (crbug.com/442785)
363 #if defined(OS_MACOSX) || defined(OS_WIN)
364 #define MAYBE_ProcessOverflowCommandLine DISABLED_ProcessOverflow
365 #else
366 #define MAYBE_ProcessOverflowCommandLine ProcessOverflow
367 #endif
369 // Variation of the ProcessOverflow test, which is driven through command line
370 // parameter instead of direct function call into the class.
371 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTestWithCommandLine,
372 MAYBE_ProcessOverflowCommandLine) {
373 TestProcessOverflow();
376 // Ensure that DevTools opened to debug DevTools is launched in a separate
377 // process when --process-per-tab is set. See crbug.com/69873.
378 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest,
379 DevToolsOnSelfInOwnProcessPPT) {
380 #if defined(OS_WIN) && defined(USE_ASH)
381 // Disable this test in Metro+Ash for now (http://crbug.com/262796).
382 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
383 switches::kAshBrowserTests))
384 return;
385 #endif
387 base::CommandLine& parsed_command_line =
388 *base::CommandLine::ForCurrentProcess();
389 parsed_command_line.AppendSwitch(switches::kProcessPerTab);
391 int tab_count = 1;
392 int host_count = 1;
394 GURL page1("data:text/html,hello world1");
395 ui_test_utils::WindowedTabAddedNotificationObserver observer1(
396 content::NotificationService::AllSources());
397 chrome::ShowSingletonTab(browser(), page1);
398 observer1.Wait();
399 tab_count++;
400 host_count++;
401 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
402 EXPECT_EQ(host_count, RenderProcessHostCount());
404 // DevTools start in docked mode (no new tab), in a separate process.
405 chrome::ToggleDevToolsWindow(browser(), DevToolsToggleAction::Inspect());
406 host_count++;
407 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
408 EXPECT_EQ(host_count, RenderProcessHostCount());
410 WebContents* devtools = FindFirstDevToolsContents();
411 DCHECK(devtools);
413 // DevTools start in a separate process.
414 DevToolsWindow::OpenDevToolsWindow(devtools, DevToolsToggleAction::Inspect());
415 host_count++;
416 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
417 EXPECT_EQ(host_count, RenderProcessHostCount());
419 // close docked devtools
420 content::WindowedNotificationObserver close_observer(
421 content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
422 content::Source<WebContents>(devtools));
424 chrome::ToggleDevToolsWindow(browser(), DevToolsToggleAction::Toggle());
425 close_observer.Wait();
428 // Ensure that DevTools opened to debug DevTools is launched in a separate
429 // process. See crbug.com/69873.
430 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest,
431 DevToolsOnSelfInOwnProcess) {
432 #if defined(OS_WIN) && defined(USE_ASH)
433 // Disable this test in Metro+Ash for now (http://crbug.com/262796).
434 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
435 switches::kAshBrowserTests))
436 return;
437 #endif
439 int tab_count = 1;
440 int host_count = 1;
442 GURL page1("data:text/html,hello world1");
443 ui_test_utils::WindowedTabAddedNotificationObserver observer1(
444 content::NotificationService::AllSources());
445 chrome::ShowSingletonTab(browser(), page1);
446 observer1.Wait();
447 tab_count++;
448 host_count++;
449 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
450 EXPECT_EQ(host_count, RenderProcessHostCount());
452 // DevTools start in docked mode (no new tab), in a separate process.
453 chrome::ToggleDevToolsWindow(browser(), DevToolsToggleAction::Inspect());
454 host_count++;
455 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
456 EXPECT_EQ(host_count, RenderProcessHostCount());
458 WebContents* devtools = FindFirstDevToolsContents();
459 DCHECK(devtools);
461 // DevTools start in a separate process.
462 DevToolsWindow::OpenDevToolsWindow(devtools, DevToolsToggleAction::Inspect());
463 host_count++;
464 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
465 EXPECT_EQ(host_count, RenderProcessHostCount());
467 // close docked devtools
468 content::WindowedNotificationObserver close_observer(
469 content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
470 content::Source<content::WebContents>(devtools));
471 chrome::ToggleDevToolsWindow(browser(), DevToolsToggleAction::Toggle());
472 close_observer.Wait();
475 // This class's goal is to close the browser window when a renderer process has
476 // crashed. It does so by monitoring WebContents for RenderProcessGone event and
477 // closing the passed in TabStripModel. This is used in the following test case.
478 class WindowDestroyer : public content::WebContentsObserver {
479 public:
480 WindowDestroyer(content::WebContents* web_contents, TabStripModel* model)
481 : content::WebContentsObserver(web_contents),
482 tab_strip_model_(model) {
485 void RenderProcessGone(base::TerminationStatus status) override {
486 // Wait for the window to be destroyed, which will ensure all other
487 // RenderViewHost objects are deleted before we return and proceed with
488 // the next iteration of notifications.
489 content::WindowedNotificationObserver observer(
490 chrome::NOTIFICATION_BROWSER_CLOSED,
491 content::NotificationService::AllSources());
492 tab_strip_model_->CloseAllTabs();
493 observer.Wait();
496 private:
497 TabStripModel* tab_strip_model_;
499 DISALLOW_COPY_AND_ASSIGN(WindowDestroyer);
502 // Test to ensure that while iterating through all listeners in
503 // RenderProcessHost and invalidating them, we remove them properly and don't
504 // access already freed objects. See http://crbug.com/255524.
505 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest,
506 CloseAllTabsDuringProcessDied) {
507 GURL url(chrome::kChromeUIOmniboxURL);
509 ui_test_utils::NavigateToURL(browser(), url);
510 ui_test_utils::NavigateToURLWithDisposition(
511 browser(), url, NEW_BACKGROUND_TAB,
512 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
514 EXPECT_EQ(2, browser()->tab_strip_model()->count());
516 WebContents* wc1 = browser()->tab_strip_model()->GetWebContentsAt(0);
517 WebContents* wc2 = browser()->tab_strip_model()->GetWebContentsAt(1);
518 EXPECT_EQ(wc1->GetRenderProcessHost(), wc2->GetRenderProcessHost());
520 // Create an object that will close the window on a process crash.
521 WindowDestroyer destroyer(wc1, browser()->tab_strip_model());
523 content::WindowedNotificationObserver observer(
524 chrome::NOTIFICATION_BROWSER_CLOSED,
525 content::NotificationService::AllSources());
527 // Kill the renderer process, simulating a crash. This should the ProcessDied
528 // method to be called. Alternatively, RenderProcessHost::OnChannelError can
529 // be called to directly force a call to ProcessDied.
530 wc1->GetRenderProcessHost()->Shutdown(-1, true);
532 observer.Wait();