Upstream version 5.34.104.0
[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(content::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     WaitForLauncherThread();
75     return wc->GetRenderProcessHost()->GetHandle();
76   }
77
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);
83
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);
88
89     WaitForLauncherThread();
90     return wc->GetRenderProcessHost()->GetHandle();
91   }
92
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();
99   }
100
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;
113
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());
122
123     // Create a new TYPE_TABBED tab.  It should be in its own process.
124     GURL page1("data:text/html,hello world1");
125
126     ui_test_utils::WindowedTabAddedNotificationObserver observer1(
127         content::NotificationService::AllSources());
128     chrome::ShowSingletonTab(browser(), page1);
129     observer1.Wait();
130
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);
139
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);
152
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);
168
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);
185   }
186 };
187
188
189 class ChromeRenderProcessHostTestWithCommandLine
190     : public ChromeRenderProcessHostTest {
191  protected:
192   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
193     command_line->AppendSwitchASCII(switches::kRendererProcessLimit, "1");
194   }
195 };
196
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);
200
201   CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
202   parsed_command_line.AppendSwitch(switches::kProcessPerTab);
203
204   int tab_count = 1;
205   int host_count = 1;
206
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());
212
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());
223
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());
233
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());
242
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());
251 }
252
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;
260   }
261   CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
262   parsed_command_line.AppendSwitch(switches::kProcessPerTab);
263
264   // Change the first tab to be the omnibox page (TYPE_WEBUI).
265   GURL omnibox(chrome::kChromeUIOmniboxURL);
266   ui_test_utils::NavigateToURL(browser(), omnibox);
267
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());
272
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());
280
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());
290
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());
297 }
298 #endif
299
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
306
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();
311 }
312
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();
318 }
319
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
329
330   CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
331   parsed_command_line.AppendSwitch(switches::kProcessPerTab);
332
333   int tab_count = 1;
334   int host_count = 1;
335
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());
345
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());
351
352   RenderViewHost* devtools = FindFirstDevToolsHost();
353   DCHECK(devtools);
354
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());
360
361   // close docked devtools
362   content::WindowedNotificationObserver close_observer(
363       content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
364       content::Source<WebContents>(WebContents::FromRenderViewHost(devtools)));
365
366   chrome::ToggleDevToolsWindow(browser(), DevToolsToggleAction::Toggle());
367   close_observer.Wait();
368 }
369
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
379
380   int tab_count = 1;
381   int host_count = 1;
382
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());
392
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());
398
399   RenderViewHost* devtools = FindFirstDevToolsHost();
400   DCHECK(devtools);
401
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());
407
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();
415 }
416
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) {
425   }
426
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();
436   }
437
438  private:
439   TabStripModel* tab_strip_model_;
440
441   DISALLOW_COPY_AND_ASSIGN(WindowDestroyer);
442 };
443
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);
450
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);
455
456   EXPECT_EQ(2, browser()->tab_strip_model()->count());
457
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());
461
462   // Create an object that will close the window on a process crash.
463   WindowDestroyer destroyer(wc1, browser()->tab_strip_model());
464
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());
471
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);
476
477   observer.Wait();
478 }