- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / renderer_host / render_process_host_chrome_browsertest.cc
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/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
25 using content::RenderViewHost;
26 using content::RenderWidgetHost;
27 using content::WebContents;
28
29 namespace {
30
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();
39   }
40   return count;
41 }
42
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;
56   }
57   return NULL;
58 }
59
60 }  // namespace
61
62 class ChromeRenderProcessHostTest : public InProcessBrowserTest {
63  public:
64   ChromeRenderProcessHostTest() {}
65
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);
73
74     // Ensure that the backgrounding / foregrounding gets a chance to run.
75     content::BrowserThread::PostTaskAndReply(
76         content::BrowserThread::PROCESS_LAUNCHER, FROM_HERE,
77         base::Bind(&base::DoNothing), base::MessageLoop::QuitClosure());
78     base::MessageLoop::current()->Run();
79
80     return wc->GetRenderProcessHost()->GetHandle();
81   }
82
83   // When we hit the max number of renderers, verify that the way we do process
84   // sharing behaves correctly.  In particular, this test is verifying that even
85   // when we hit the max process limit, that renderers of each type will wind up
86   // in a process of that type, even if that means creating a new process.
87   void TestProcessOverflow() {
88     int tab_count = 1;
89     int host_count = 1;
90     WebContents* tab1 = NULL;
91     WebContents* tab2 = NULL;
92     content::RenderProcessHost* rph1 = NULL;
93     content::RenderProcessHost* rph2 = NULL;
94     content::RenderProcessHost* rph3 = NULL;
95
96     // Change the first tab to be the new tab page (TYPE_WEBUI).
97     GURL newtab(chrome::kChromeUINewTabURL);
98     ui_test_utils::NavigateToURL(browser(), newtab);
99     EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
100     tab1 = browser()->tab_strip_model()->GetWebContentsAt(tab_count - 1);
101     rph1 = tab1->GetRenderProcessHost();
102     EXPECT_TRUE(chrome::IsNTPURL(tab1->GetURL(), browser()->profile()));
103     EXPECT_EQ(host_count, RenderProcessHostCount());
104
105     // Create a new TYPE_TABBED tab.  It should be in its own process.
106     GURL page1("data:text/html,hello world1");
107
108     ui_test_utils::WindowedTabAddedNotificationObserver observer1(
109         content::NotificationService::AllSources());
110     chrome::ShowSingletonTab(browser(), page1);
111     observer1.Wait();
112
113     tab_count++;
114     host_count++;
115     EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
116     tab1 = browser()->tab_strip_model()->GetWebContentsAt(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     ui_test_utils::WindowedTabAddedNotificationObserver observer2(
125         content::NotificationService::AllSources());
126     chrome::ShowSingletonTab(browser(), page2);
127     observer2.Wait();
128     tab_count++;
129     EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
130     tab2 = browser()->tab_strip_model()->GetWebContentsAt(tab_count - 1);
131     EXPECT_EQ(tab2->GetURL(), page2);
132     EXPECT_EQ(host_count, RenderProcessHostCount());
133     EXPECT_EQ(tab2->GetRenderProcessHost(), rph2);
134
135     // Create another TYPE_WEBUI tab.  It should share the process with newtab.
136     // Note: intentionally create this tab after the TYPE_TABBED tabs to
137     // exercise bug 43448 where extension and WebUI tabs could get combined into
138     // normal renderers.
139     GURL history(chrome::kChromeUIHistoryURL);
140     ui_test_utils::WindowedTabAddedNotificationObserver observer3(
141         content::NotificationService::AllSources());
142     chrome::ShowSingletonTab(browser(), history);
143     observer3.Wait();
144     tab_count++;
145     EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
146     tab2 = browser()->tab_strip_model()->GetWebContentsAt(tab_count - 1);
147     EXPECT_EQ(tab2->GetURL(), GURL(history));
148     EXPECT_EQ(host_count, RenderProcessHostCount());
149     EXPECT_EQ(tab2->GetRenderProcessHost(), rph1);
150
151     // Create a TYPE_EXTENSION tab.  It should be in its own process.
152     // (the bookmark manager is implemented as an extension)
153     GURL bookmarks(chrome::kChromeUIBookmarksURL);
154     ui_test_utils::WindowedTabAddedNotificationObserver observer4(
155         content::NotificationService::AllSources());
156     chrome::ShowSingletonTab(browser(), bookmarks);
157     observer4.Wait();
158     tab_count++;
159     host_count++;
160     EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
161     tab1 = browser()->tab_strip_model()->GetWebContentsAt(tab_count - 1);
162     rph3 = tab1->GetRenderProcessHost();
163     EXPECT_EQ(tab1->GetURL(), bookmarks);
164     EXPECT_EQ(host_count, RenderProcessHostCount());
165     EXPECT_NE(rph1, rph3);
166     EXPECT_NE(rph2, rph3);
167   }
168 };
169
170
171 class ChromeRenderProcessHostTestWithCommandLine
172     : public ChromeRenderProcessHostTest {
173  protected:
174   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
175     command_line->AppendSwitchASCII(switches::kRendererProcessLimit, "1");
176   }
177 };
178
179 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, ProcessPerTab) {
180   // Set max renderers to 1 to force running out of processes.
181   content::RenderProcessHost::SetMaxRendererProcessCount(1);
182
183   CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
184   parsed_command_line.AppendSwitch(switches::kProcessPerTab);
185
186   int tab_count = 1;
187   int host_count = 1;
188
189   // Change the first tab to be the new tab page (TYPE_WEBUI).
190   GURL newtab(chrome::kChromeUINewTabURL);
191   ui_test_utils::NavigateToURL(browser(), newtab);
192   EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
193   EXPECT_EQ(host_count, RenderProcessHostCount());
194
195   // Create a new TYPE_TABBED tab.  It should be in its own process.
196   GURL page1("data:text/html,hello world1");
197   ui_test_utils::WindowedTabAddedNotificationObserver observer1(
198       content::NotificationService::AllSources());
199   chrome::ShowSingletonTab(browser(), page1);
200   observer1.Wait();
201   tab_count++;
202   host_count++;
203   EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
204   EXPECT_EQ(host_count, RenderProcessHostCount());
205
206   // Create another TYPE_TABBED tab.  It should share the previous process.
207   GURL page2("data:text/html,hello world2");
208   ui_test_utils::WindowedTabAddedNotificationObserver observer2(
209       content::NotificationService::AllSources());
210   chrome::ShowSingletonTab(browser(), page2);
211   observer2.Wait();
212   tab_count++;
213   EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
214   EXPECT_EQ(host_count, RenderProcessHostCount());
215
216   // Create another new tab.  It should share the process with the other WebUI.
217   ui_test_utils::WindowedTabAddedNotificationObserver observer3(
218       content::NotificationService::AllSources());
219   chrome::NewTab(browser());
220   observer3.Wait();
221   tab_count++;
222   EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
223   EXPECT_EQ(host_count, RenderProcessHostCount());
224
225   // Create another new tab.  It should share the process with the other WebUI.
226   ui_test_utils::WindowedTabAddedNotificationObserver observer4(
227       content::NotificationService::AllSources());
228   chrome::NewTab(browser());
229   observer4.Wait();
230   tab_count++;
231   EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
232   EXPECT_EQ(host_count, RenderProcessHostCount());
233 }
234
235 // We don't change process priorities on Mac or Posix because the user lacks the
236 // permission to raise a process' priority even after lowering it.
237 #if defined(OS_WIN) || defined(OS_LINUX)
238 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, Backgrounding) {
239   if (!base::Process::CanBackgroundProcesses()) {
240     LOG(ERROR) << "Can't background processes";
241     return;
242   }
243   CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
244   parsed_command_line.AppendSwitch(switches::kProcessPerTab);
245
246   // Change the first tab to be the new tab page (TYPE_WEBUI).
247   GURL newtab(chrome::kChromeUINewTabURL);
248   ui_test_utils::NavigateToURL(browser(), newtab);
249
250   // Create a new tab. It should be foreground.
251   GURL page1("data:text/html,hello world1");
252   base::ProcessHandle pid1 = ShowSingletonTab(page1);
253   EXPECT_FALSE(base::Process(pid1).IsProcessBackgrounded());
254
255   // Create another tab. It should be foreground, and the first tab should
256   // now be background.
257   GURL page2("data:text/html,hello world2");
258   base::ProcessHandle pid2 = ShowSingletonTab(page2);
259   EXPECT_NE(pid1, pid2);
260   EXPECT_TRUE(base::Process(pid1).IsProcessBackgrounded());
261   EXPECT_FALSE(base::Process(pid2).IsProcessBackgrounded());
262
263   // Navigate back to first page. It should be foreground again, and the second
264   // tab should be background.
265   EXPECT_EQ(pid1, ShowSingletonTab(page1));
266   EXPECT_FALSE(base::Process(pid1).IsProcessBackgrounded());
267   EXPECT_TRUE(base::Process(pid2).IsProcessBackgrounded());
268 }
269 #endif
270
271 // TODO(nasko): crbug.com/173137
272 #if defined(OS_WIN)
273 #define MAYBE_ProcessOverflow DISABLED_ProcessOverflow
274 #else
275 #define MAYBE_ProcessOverflow ProcessOverflow
276 #endif
277
278 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, MAYBE_ProcessOverflow) {
279   // Set max renderers to 1 to force running out of processes.
280   content::RenderProcessHost::SetMaxRendererProcessCount(1);
281   TestProcessOverflow();
282 }
283
284 // Variation of the ProcessOverflow test, which is driven through command line
285 // parameter instead of direct function call into the class.
286 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTestWithCommandLine,
287                        ProcessOverflow) {
288   TestProcessOverflow();
289 }
290
291 // Ensure that DevTools opened to debug DevTools is launched in a separate
292 // process when --process-per-tab is set. See crbug.com/69873.
293 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest,
294                        DevToolsOnSelfInOwnProcessPPT) {
295 #if defined(OS_WIN) && defined(USE_ASH)
296   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
297   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
298     return;
299 #endif
300
301   CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
302   parsed_command_line.AppendSwitch(switches::kProcessPerTab);
303
304   int tab_count = 1;
305   int host_count = 1;
306
307   GURL page1("data:text/html,hello world1");
308   ui_test_utils::WindowedTabAddedNotificationObserver observer1(
309       content::NotificationService::AllSources());
310   chrome::ShowSingletonTab(browser(), page1);
311   observer1.Wait();
312   tab_count++;
313   host_count++;
314   EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
315   EXPECT_EQ(host_count, RenderProcessHostCount());
316
317   // DevTools start in docked mode (no new tab), in a separate process.
318   chrome::ToggleDevToolsWindow(browser(), DevToolsToggleAction::Inspect());
319   host_count++;
320   EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
321   EXPECT_EQ(host_count, RenderProcessHostCount());
322
323   RenderViewHost* devtools = FindFirstDevToolsHost();
324   DCHECK(devtools);
325
326   // DevTools start in a separate process.
327   DevToolsWindow::ToggleDevToolsWindow(
328       devtools, true, DevToolsToggleAction::Inspect());
329   host_count++;
330   EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
331   EXPECT_EQ(host_count, RenderProcessHostCount());
332
333   // close docked devtools
334   content::WindowedNotificationObserver close_observer(
335       content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
336       content::Source<WebContents>(WebContents::FromRenderViewHost(devtools)));
337
338   chrome::ToggleDevToolsWindow(browser(), DevToolsToggleAction::Toggle());
339   close_observer.Wait();
340 }
341
342 // Ensure that DevTools opened to debug DevTools is launched in a separate
343 // process. See crbug.com/69873.
344 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest,
345                        DevToolsOnSelfInOwnProcess) {
346 #if defined(OS_WIN) && defined(USE_ASH)
347   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
348   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
349     return;
350 #endif
351
352   int tab_count = 1;
353   int host_count = 1;
354
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());
364
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());
370
371   RenderViewHost* devtools = FindFirstDevToolsHost();
372   DCHECK(devtools);
373
374   // DevTools start in a separate process.
375   DevToolsWindow::ToggleDevToolsWindow(
376       devtools, true, DevToolsToggleAction::Inspect());
377   host_count++;
378   EXPECT_EQ(tab_count, browser()->tab_strip_model()->count());
379   EXPECT_EQ(host_count, RenderProcessHostCount());
380
381   // close docked devtools
382   content::WindowedNotificationObserver close_observer(
383       content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
384       content::Source<content::WebContents>(
385           WebContents::FromRenderViewHost(devtools)));
386   chrome::ToggleDevToolsWindow(browser(), DevToolsToggleAction::Toggle());
387   close_observer.Wait();
388 }
389
390 // This class's goal is to close the browser window when a renderer process has
391 // crashed. It does so by monitoring WebContents for RenderProcessGone event and
392 // closing the passed in TabStripModel. This is used in the following test case.
393 class WindowDestroyer : public content::WebContentsObserver {
394  public:
395   WindowDestroyer(content::WebContents* web_contents, TabStripModel* model)
396       : content::WebContentsObserver(web_contents),
397         tab_strip_model_(model) {
398   }
399
400   virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE {
401     // Wait for the window to be destroyed, which will ensure all other
402     // RenderViewHost objects are deleted before we return and proceed with
403     // the next iteration of notifications.
404     content::WindowedNotificationObserver observer(
405         chrome::NOTIFICATION_BROWSER_CLOSED,
406         content::NotificationService::AllSources());
407     tab_strip_model_->CloseAllTabs();
408     observer.Wait();
409   }
410
411  private:
412   TabStripModel* tab_strip_model_;
413
414   DISALLOW_COPY_AND_ASSIGN(WindowDestroyer);
415 };
416
417 // Test to ensure that while iterating through all listeners in
418 // RenderProcessHost and invalidating them, we remove them properly and don't
419 // access already freed objects. See http://crbug.com/255524.
420 IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest,
421                        CloseAllTabsDuringProcessDied) {
422   GURL url(chrome::kChromeUINewTabURL);
423
424   ui_test_utils::NavigateToURL(browser(), url);
425   ui_test_utils::NavigateToURLWithDisposition(
426       browser(), url, NEW_BACKGROUND_TAB,
427       ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
428
429   EXPECT_EQ(2, browser()->tab_strip_model()->count());
430
431   WebContents* wc1 = browser()->tab_strip_model()->GetWebContentsAt(0);
432   WebContents* wc2 = browser()->tab_strip_model()->GetWebContentsAt(1);
433   EXPECT_EQ(wc1->GetRenderProcessHost(), wc2->GetRenderProcessHost());
434
435   // Create an object that will close the window on a process crash.
436   WindowDestroyer destroyer(wc1, browser()->tab_strip_model());
437
438   // Use NOTIFICATION_BROWSER_CLOSED instead of NOTIFICATION_WINDOW_CLOSED,
439   // since the latter is not implemented on OSX and the test will timeout,
440   // causing it to fail.
441   content::WindowedNotificationObserver observer(
442       chrome::NOTIFICATION_BROWSER_CLOSED,
443       content::NotificationService::AllSources());
444
445   // Kill the renderer process, simulating a crash. This should the ProcessDied
446   // method to be called. Alternatively, RenderProcessHost::OnChannelError can
447   // be called to directly force a call to ProcessDied.
448   base::KillProcess(wc1->GetRenderProcessHost()->GetHandle(), -1, true);
449
450   observer.Wait();
451 }