Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / renderer_host / render_process_host_chrome_browsertest.cc
blob55cf996f5778ef8d0f005007c2cfefd961815890
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"
25 using content::RenderViewHost;
26 using content::RenderWidgetHost;
27 using content::WebContents;
29 namespace {
31 int RenderProcessHostCount() {
32 content::RenderProcessHost::iterator hosts =
33 content::RenderProcessHost::AllHostsIterator();
34 int count = 0;
35 while (!hosts.IsAtEnd()) {
36 if (hosts.GetCurrentValue()->HasConnection())
37 count++;
38 hosts.Advance();
40 return count;
43 RenderViewHost* FindFirstDevToolsHost() {
44 scoped_ptr<content::RenderWidgetHostIterator> widgets(
45 RenderWidgetHost::GetRenderWidgetHosts());
46 while (content::RenderWidgetHost* widget = widgets->GetNextHost()) {
47 if (!widget->GetProcess()->HasConnection())
48 continue;
49 if (!widget->IsRenderView())
50 continue;
51 RenderViewHost* host = RenderViewHost::From(widget);
52 WebContents* contents = WebContents::FromRenderViewHost(host);
53 GURL url = contents->GetURL();
54 if (url.SchemeIs(chrome::kChromeDevToolsScheme))
55 return host;
57 return NULL;
60 } // namespace
62 class ChromeRenderProcessHostTest : public InProcessBrowserTest {
63 public:
64 ChromeRenderProcessHostTest() {}
66 // Show a tab, activating the current one if there is one, and wait for
67 // the renderer process to be created or foregrounded, returning the process
68 // handle.
69 base::ProcessHandle ShowSingletonTab(const GURL& page) {
70 chrome::ShowSingletonTab(browser(), page);
71 WebContents* wc = browser()->tab_strip_model()->GetActiveWebContents();
72 CHECK(wc->GetURL() == page);
74 WaitForLauncherThread();
75 return wc->GetRenderProcessHost()->GetHandle();
78 // Loads the given url in a new background tab and returns the handle of its
79 // renderer.
80 base::ProcessHandle OpenBackgroundTab(const GURL& page) {
81 ui_test_utils::NavigateToURLWithDisposition(browser(), page,
82 NEW_BACKGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
84 TabStripModel* tab_strip = browser()->tab_strip_model();
85 WebContents* wc = tab_strip->GetWebContentsAt(
86 tab_strip->active_index() + 1);
87 CHECK(wc->GetVisibleURL() == page);
89 WaitForLauncherThread();
90 return wc->GetRenderProcessHost()->GetHandle();
93 // Ensures that the backgrounding / foregrounding gets a chance to run.
94 void WaitForLauncherThread() {
95 content::BrowserThread::PostTaskAndReply(
96 content::BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
97 base::Bind(&base::DoNothing), base::MessageLoop::QuitClosure());
98 base::MessageLoop::current()->Run();
101 // When we hit the max number of renderers, verify that the way we do process
102 // sharing behaves correctly. In particular, this test is verifying that even
103 // when we hit the max process limit, that renderers of each type will wind up
104 // in a process of that type, even if that means creating a new process.
105 void TestProcessOverflow() {
106 int tab_count = 1;
107 int host_count = 1;
108 WebContents* tab1 = NULL;
109 WebContents* tab2 = NULL;
110 content::RenderProcessHost* rph1 = NULL;
111 content::RenderProcessHost* rph2 = NULL;
112 content::RenderProcessHost* rph3 = NULL;
114 // Change the first tab to be the omnibox page (TYPE_WEBUI).
115 GURL omnibox(chrome::kChromeUIOmniboxURL);
116 ui_test_utils::NavigateToURL(browser(), omnibox);
117 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
118 tab1 = browser()->tab_strip_model()->GetWebContentsAt(tab_count - 1);
119 rph1 = tab1->GetRenderProcessHost();
120 EXPECT_EQ(omnibox, tab1->GetURL());
121 EXPECT_EQ(host_count, RenderProcessHostCount());
123 // Create a new TYPE_TABBED tab. It should be in its own process.
124 GURL page1("data:text/html,hello world1");
126 ui_test_utils::WindowedTabAddedNotificationObserver observer1(
127 content::NotificationService::AllSources());
128 chrome::ShowSingletonTab(browser(), page1);
129 observer1.Wait();
131 tab_count++;
132 host_count++;
133 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
134 tab1 = browser()->tab_strip_model()->GetWebContentsAt(tab_count - 1);
135 rph2 = tab1->GetRenderProcessHost();
136 EXPECT_EQ(tab1->GetURL(), page1);
137 EXPECT_EQ(host_count, RenderProcessHostCount());
138 EXPECT_NE(rph1, rph2);
140 // Create another TYPE_TABBED tab. It should share the previous process.
141 GURL page2("data:text/html,hello world2");
142 ui_test_utils::WindowedTabAddedNotificationObserver observer2(
143 content::NotificationService::AllSources());
144 chrome::ShowSingletonTab(browser(), page2);
145 observer2.Wait();
146 tab_count++;
147 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
148 tab2 = browser()->tab_strip_model()->GetWebContentsAt(tab_count - 1);
149 EXPECT_EQ(tab2->GetURL(), page2);
150 EXPECT_EQ(host_count, RenderProcessHostCount());
151 EXPECT_EQ(tab2->GetRenderProcessHost(), rph2);
153 // Create another TYPE_WEBUI tab. It should share the process with omnibox.
154 // Note: intentionally create this tab after the TYPE_TABBED tabs to
155 // exercise bug 43448 where extension and WebUI tabs could get combined into
156 // normal renderers.
157 GURL history(chrome::kChromeUIHistoryURL);
158 ui_test_utils::WindowedTabAddedNotificationObserver observer3(
159 content::NotificationService::AllSources());
160 chrome::ShowSingletonTab(browser(), history);
161 observer3.Wait();
162 tab_count++;
163 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
164 tab2 = browser()->tab_strip_model()->GetWebContentsAt(tab_count - 1);
165 EXPECT_EQ(tab2->GetURL(), GURL(history));
166 EXPECT_EQ(host_count, RenderProcessHostCount());
167 EXPECT_EQ(tab2->GetRenderProcessHost(), rph1);
169 // Create a TYPE_EXTENSION tab. It should be in its own process.
170 // (the bookmark manager is implemented as an extension)
171 GURL bookmarks(chrome::kChromeUIBookmarksURL);
172 ui_test_utils::WindowedTabAddedNotificationObserver observer4(
173 content::NotificationService::AllSources());
174 chrome::ShowSingletonTab(browser(), bookmarks);
175 observer4.Wait();
176 tab_count++;
177 host_count++;
178 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
179 tab1 = browser()->tab_strip_model()->GetWebContentsAt(tab_count - 1);
180 rph3 = tab1->GetRenderProcessHost();
181 EXPECT_EQ(tab1->GetURL(), bookmarks);
182 EXPECT_EQ(host_count, RenderProcessHostCount());
183 EXPECT_NE(rph1, rph3);
184 EXPECT_NE(rph2, rph3);
189 class ChromeRenderProcessHostTestWithCommandLine
190 : public ChromeRenderProcessHostTest {
191 protected:
192 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
193 command_line->AppendSwitchASCII(switches::kRendererProcessLimit, "1");
197 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, ProcessPerTab) {
198 // Set max renderers to 1 to force running out of processes.
199 content::RenderProcessHost::SetMaxRendererProcessCount(1);
201 CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
202 parsed_command_line.AppendSwitch(switches::kProcessPerTab);
204 int tab_count = 1;
205 int host_count = 1;
207 // Change the first tab to be the new tab page (TYPE_WEBUI).
208 GURL omnibox(chrome::kChromeUIOmniboxURL);
209 ui_test_utils::NavigateToURL(browser(), omnibox);
210 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
211 EXPECT_EQ(host_count, RenderProcessHostCount());
213 // Create a new TYPE_TABBED tab. It should be in its own process.
214 GURL page1("data:text/html,hello world1");
215 ui_test_utils::WindowedTabAddedNotificationObserver observer1(
216 content::NotificationService::AllSources());
217 chrome::ShowSingletonTab(browser(), page1);
218 observer1.Wait();
219 tab_count++;
220 host_count++;
221 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
222 EXPECT_EQ(host_count, RenderProcessHostCount());
224 // Create another TYPE_TABBED tab. It should share the previous process.
225 GURL page2("data:text/html,hello world2");
226 ui_test_utils::WindowedTabAddedNotificationObserver observer2(
227 content::NotificationService::AllSources());
228 chrome::ShowSingletonTab(browser(), page2);
229 observer2.Wait();
230 tab_count++;
231 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
232 EXPECT_EQ(host_count, RenderProcessHostCount());
234 // Create another omnibox tab. It should share the process with the other
235 // WebUI.
236 ui_test_utils::NavigateToURLWithDisposition(
237 browser(), omnibox, NEW_FOREGROUND_TAB,
238 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
239 tab_count++;
240 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
241 EXPECT_EQ(host_count, RenderProcessHostCount());
243 // Create another omnibox tab. It should share the process with the other
244 // WebUI.
245 ui_test_utils::NavigateToURLWithDisposition(
246 browser(), omnibox, NEW_FOREGROUND_TAB,
247 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
248 tab_count++;
249 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
250 EXPECT_EQ(host_count, RenderProcessHostCount());
253 // We don't change process priorities on Mac or Posix because the user lacks the
254 // permission to raise a process' priority even after lowering it.
255 #if defined(OS_WIN) || defined(OS_LINUX)
256 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, Backgrounding) {
257 if (!base::Process::CanBackgroundProcesses()) {
258 LOG(ERROR) << "Can't background processes";
259 return;
261 CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
262 parsed_command_line.AppendSwitch(switches::kProcessPerTab);
264 // Change the first tab to be the omnibox page (TYPE_WEBUI).
265 GURL omnibox(chrome::kChromeUIOmniboxURL);
266 ui_test_utils::NavigateToURL(browser(), omnibox);
268 // Create a new tab. It should be foreground.
269 GURL page1("data:text/html,hello world1");
270 base::ProcessHandle pid1 = ShowSingletonTab(page1);
271 EXPECT_FALSE(base::Process(pid1).IsProcessBackgrounded());
273 // Create another tab. It should be foreground, and the first tab should
274 // now be background.
275 GURL page2("data:text/html,hello world2");
276 base::ProcessHandle pid2 = ShowSingletonTab(page2);
277 EXPECT_NE(pid1, pid2);
278 EXPECT_TRUE(base::Process(pid1).IsProcessBackgrounded());
279 EXPECT_FALSE(base::Process(pid2).IsProcessBackgrounded());
281 // Load another tab in background. The renderer of the new tab should be
282 // backgrounded, while visibility of the other renderers should not change.
283 GURL page3("data:text/html,hello world3");
284 base::ProcessHandle pid3 = OpenBackgroundTab(page3);
285 EXPECT_NE(pid3, pid1);
286 EXPECT_NE(pid3, pid2);
287 EXPECT_TRUE(base::Process(pid1).IsProcessBackgrounded());
288 EXPECT_FALSE(base::Process(pid2).IsProcessBackgrounded());
289 EXPECT_TRUE(base::Process(pid3).IsProcessBackgrounded());
291 // Navigate back to the first page. Its renderer should be in foreground
292 // again while the other renderers should be backgrounded.
293 EXPECT_EQ(pid1, ShowSingletonTab(page1));
294 EXPECT_FALSE(base::Process(pid1).IsProcessBackgrounded());
295 EXPECT_TRUE(base::Process(pid2).IsProcessBackgrounded());
296 EXPECT_TRUE(base::Process(pid3).IsProcessBackgrounded());
298 #endif
300 // TODO(nasko): crbug.com/173137
301 #if defined(OS_WIN)
302 #define MAYBE_ProcessOverflow DISABLED_ProcessOverflow
303 #else
304 #define MAYBE_ProcessOverflow ProcessOverflow
305 #endif
307 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, MAYBE_ProcessOverflow) {
308 // Set max renderers to 1 to force running out of processes.
309 content::RenderProcessHost::SetMaxRendererProcessCount(1);
310 TestProcessOverflow();
313 // Variation of the ProcessOverflow test, which is driven through command line
314 // parameter instead of direct function call into the class.
315 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTestWithCommandLine,
316 ProcessOverflow) {
317 TestProcessOverflow();
320 // Ensure that DevTools opened to debug DevTools is launched in a separate
321 // process when --process-per-tab is set. See crbug.com/69873.
322 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest,
323 DevToolsOnSelfInOwnProcessPPT) {
324 #if defined(OS_WIN) && defined(USE_ASH)
325 // Disable this test in Metro+Ash for now (http://crbug.com/262796).
326 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
327 return;
328 #endif
330 CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
331 parsed_command_line.AppendSwitch(switches::kProcessPerTab);
333 int tab_count = 1;
334 int host_count = 1;
336 GURL page1("data:text/html,hello world1");
337 ui_test_utils::WindowedTabAddedNotificationObserver observer1(
338 content::NotificationService::AllSources());
339 chrome::ShowSingletonTab(browser(), page1);
340 observer1.Wait();
341 tab_count++;
342 host_count++;
343 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
344 EXPECT_EQ(host_count, RenderProcessHostCount());
346 // DevTools start in docked mode (no new tab), in a separate process.
347 chrome::ToggleDevToolsWindow(browser(), DevToolsToggleAction::Inspect());
348 host_count++;
349 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
350 EXPECT_EQ(host_count, RenderProcessHostCount());
352 RenderViewHost* devtools = FindFirstDevToolsHost();
353 DCHECK(devtools);
355 // DevTools start in a separate process.
356 DevToolsWindow::OpenDevToolsWindow(devtools, DevToolsToggleAction::Inspect());
357 host_count++;
358 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
359 EXPECT_EQ(host_count, RenderProcessHostCount());
361 // close docked devtools
362 content::WindowedNotificationObserver close_observer(
363 content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
364 content::Source<WebContents>(WebContents::FromRenderViewHost(devtools)));
366 chrome::ToggleDevToolsWindow(browser(), DevToolsToggleAction::Toggle());
367 close_observer.Wait();
370 // Ensure that DevTools opened to debug DevTools is launched in a separate
371 // process. See crbug.com/69873.
372 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest,
373 DevToolsOnSelfInOwnProcess) {
374 #if defined(OS_WIN) && defined(USE_ASH)
375 // Disable this test in Metro+Ash for now (http://crbug.com/262796).
376 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
377 return;
378 #endif
380 int tab_count = 1;
381 int host_count = 1;
383 GURL page1("data:text/html,hello world1");
384 ui_test_utils::WindowedTabAddedNotificationObserver observer1(
385 content::NotificationService::AllSources());
386 chrome::ShowSingletonTab(browser(), page1);
387 observer1.Wait();
388 tab_count++;
389 host_count++;
390 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
391 EXPECT_EQ(host_count, RenderProcessHostCount());
393 // DevTools start in docked mode (no new tab), in a separate process.
394 chrome::ToggleDevToolsWindow(browser(), DevToolsToggleAction::Inspect());
395 host_count++;
396 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
397 EXPECT_EQ(host_count, RenderProcessHostCount());
399 RenderViewHost* devtools = FindFirstDevToolsHost();
400 DCHECK(devtools);
402 // DevTools start in a separate process.
403 DevToolsWindow::OpenDevToolsWindow(devtools, DevToolsToggleAction::Inspect());
404 host_count++;
405 EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
406 EXPECT_EQ(host_count, RenderProcessHostCount());
408 // close docked devtools
409 content::WindowedNotificationObserver close_observer(
410 content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
411 content::Source<content::WebContents>(
412 WebContents::FromRenderViewHost(devtools)));
413 chrome::ToggleDevToolsWindow(browser(), DevToolsToggleAction::Toggle());
414 close_observer.Wait();
417 // This class's goal is to close the browser window when a renderer process has
418 // crashed. It does so by monitoring WebContents for RenderProcessGone event and
419 // closing the passed in TabStripModel. This is used in the following test case.
420 class WindowDestroyer : public content::WebContentsObserver {
421 public:
422 WindowDestroyer(content::WebContents* web_contents, TabStripModel* model)
423 : content::WebContentsObserver(web_contents),
424 tab_strip_model_(model) {
427 virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE {
428 // Wait for the window to be destroyed, which will ensure all other
429 // RenderViewHost objects are deleted before we return and proceed with
430 // the next iteration of notifications.
431 content::WindowedNotificationObserver observer(
432 chrome::NOTIFICATION_BROWSER_CLOSED,
433 content::NotificationService::AllSources());
434 tab_strip_model_->CloseAllTabs();
435 observer.Wait();
438 private:
439 TabStripModel* tab_strip_model_;
441 DISALLOW_COPY_AND_ASSIGN(WindowDestroyer);
444 // Test to ensure that while iterating through all listeners in
445 // RenderProcessHost and invalidating them, we remove them properly and don't
446 // access already freed objects. See http://crbug.com/255524.
447 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest,
448 CloseAllTabsDuringProcessDied) {
449 GURL url(chrome::kChromeUIOmniboxURL);
451 ui_test_utils::NavigateToURL(browser(), url);
452 ui_test_utils::NavigateToURLWithDisposition(
453 browser(), url, NEW_BACKGROUND_TAB,
454 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
456 EXPECT_EQ(2, browser()->tab_strip_model()->count());
458 WebContents* wc1 = browser()->tab_strip_model()->GetWebContentsAt(0);
459 WebContents* wc2 = browser()->tab_strip_model()->GetWebContentsAt(1);
460 EXPECT_EQ(wc1->GetRenderProcessHost(), wc2->GetRenderProcessHost());
462 // Create an object that will close the window on a process crash.
463 WindowDestroyer destroyer(wc1, browser()->tab_strip_model());
465 // Use NOTIFICATION_BROWSER_CLOSED instead of NOTIFICATION_WINDOW_CLOSED,
466 // since the latter is not implemented on OSX and the test will timeout,
467 // causing it to fail.
468 content::WindowedNotificationObserver observer(
469 chrome::NOTIFICATION_BROWSER_CLOSED,
470 content::NotificationService::AllSources());
472 // Kill the renderer process, simulating a crash. This should the ProcessDied
473 // method to be called. Alternatively, RenderProcessHost::OnChannelError can
474 // be called to directly force a call to ProcessDied.
475 base::KillProcess(wc1->GetRenderProcessHost()->GetHandle(), -1, true);
477 observer.Wait();