Index: chrome/browser/renderer_host/render_process_host_chrome_browsertest.cc |
diff --git a/chrome/browser/renderer_host/render_process_host_chrome_browsertest.cc b/chrome/browser/renderer_host/render_process_host_chrome_browsertest.cc |
index 24c58ef9a6acd950d566e73f5f62e0f5225ad8ed..30c67742daa6e7807a84e9cc3a78382d6737dc50 100644 |
--- a/chrome/browser/renderer_host/render_process_host_chrome_browsertest.cc |
+++ b/chrome/browser/renderer_host/render_process_host_chrome_browsertest.cc |
@@ -1,533 +1,684 @@ |
-// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "base/command_line.h" |
-#include "base/process/process.h" |
-#include "chrome/browser/chrome_notification_types.h" |
-#include "chrome/browser/devtools/devtools_window.h" |
-#include "chrome/browser/search/search.h" |
-#include "chrome/browser/ui/browser.h" |
-#include "chrome/browser/ui/browser_commands.h" |
-#include "chrome/browser/ui/singleton_tabs.h" |
-#include "chrome/browser/ui/tabs/tab_strip_model.h" |
-#include "chrome/common/chrome_switches.h" |
-#include "chrome/common/url_constants.h" |
-#include "chrome/test/base/in_process_browser_test.h" |
-#include "chrome/test/base/test_switches.h" |
-#include "chrome/test/base/ui_test_utils.h" |
-#include "content/public/browser/notification_service.h" |
-#include "content/public/browser/render_process_host.h" |
-#include "content/public/browser/render_view_host.h" |
-#include "content/public/browser/render_widget_host_iterator.h" |
-#include "content/public/browser/web_contents.h" |
-#include "content/public/browser/web_contents_observer.h" |
-#include "content/public/test/browser_test_utils.h" |
- |
-using content::RenderViewHost; |
-using content::RenderWidgetHost; |
-using content::WebContents; |
- |
-namespace { |
- |
-int RenderProcessHostCount() { |
- content::RenderProcessHost::iterator hosts = |
- content::RenderProcessHost::AllHostsIterator(); |
- int count = 0; |
- while (!hosts.IsAtEnd()) { |
- if (hosts.GetCurrentValue()->HasConnection()) |
- count++; |
- hosts.Advance(); |
- } |
- return count; |
-} |
- |
-WebContents* FindFirstDevToolsContents() { |
- scoped_ptr<content::RenderWidgetHostIterator> widgets( |
- RenderWidgetHost::GetRenderWidgetHosts()); |
- while (content::RenderWidgetHost* widget = widgets->GetNextHost()) { |
- if (!widget->GetProcess()->HasConnection()) |
- continue; |
- if (!widget->IsRenderView()) |
- continue; |
- RenderViewHost* host = RenderViewHost::From(widget); |
- WebContents* contents = WebContents::FromRenderViewHost(host); |
- GURL url = contents->GetURL(); |
- if (url.SchemeIs(content::kChromeDevToolsScheme)) |
- return contents; |
- } |
- return NULL; |
-} |
- |
-// TODO(rvargas) crbug.com/417532: Remove this code. |
-base::Process ProcessFromHandle(base::ProcessHandle handle) { |
-#if defined(OS_WIN) |
- if (handle == GetCurrentProcess()) |
- return base::Process::Current(); |
- |
- base::ProcessHandle out_handle; |
- if (!::DuplicateHandle(GetCurrentProcess(), handle, GetCurrentProcess(), |
- &out_handle, 0, FALSE, DUPLICATE_SAME_ACCESS)) { |
- return base::Process(); |
- } |
- handle = out_handle; |
-#endif // defined(OS_WIN) |
- return base::Process(handle); |
-} |
- |
-} // namespace |
- |
-class ChromeRenderProcessHostTest : public InProcessBrowserTest { |
- public: |
- ChromeRenderProcessHostTest() {} |
- |
- // Show a tab, activating the current one if there is one, and wait for |
- // the renderer process to be created or foregrounded, returning the process |
- // handle. |
- base::Process ShowSingletonTab(const GURL& page) { |
- chrome::ShowSingletonTab(browser(), page); |
- WebContents* wc = browser()->tab_strip_model()->GetActiveWebContents(); |
- CHECK(wc->GetURL() == page); |
- |
- WaitForLauncherThread(); |
- WaitForMessageProcessing(wc); |
- return ProcessFromHandle(wc->GetRenderProcessHost()->GetHandle()); |
- } |
- |
- // Loads the given url in a new background tab and returns the handle of its |
- // renderer. |
- base::Process OpenBackgroundTab(const GURL& page) { |
- ui_test_utils::NavigateToURLWithDisposition(browser(), page, |
- NEW_BACKGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); |
- |
- TabStripModel* tab_strip = browser()->tab_strip_model(); |
- WebContents* wc = tab_strip->GetWebContentsAt( |
- tab_strip->active_index() + 1); |
- CHECK(wc->GetVisibleURL() == page); |
- |
- WaitForLauncherThread(); |
- WaitForMessageProcessing(wc); |
- return ProcessFromHandle(wc->GetRenderProcessHost()->GetHandle()); |
- } |
- |
- // Ensures that the backgrounding / foregrounding gets a chance to run. |
- void WaitForLauncherThread() { |
- content::BrowserThread::PostTaskAndReply( |
- content::BrowserThread::PROCESS_LAUNCHER, FROM_HERE, |
- base::Bind(&base::DoNothing), base::MessageLoop::QuitClosure()); |
- base::MessageLoop::current()->Run(); |
- } |
- |
- // Implicitly waits for the renderer process associated with the specified |
- // WebContents to process outstanding IPC messages by running some JavaScript |
- // and waiting for the result. |
- void WaitForMessageProcessing(WebContents* wc) { |
- bool result = false; |
- ASSERT_TRUE(content::ExecuteScriptAndExtractBool( |
- wc, "window.domAutomationController.send(true);", &result)); |
- ASSERT_TRUE(result); |
- } |
- |
- // When we hit the max number of renderers, verify that the way we do process |
- // sharing behaves correctly. In particular, this test is verifying that even |
- // when we hit the max process limit, that renderers of each type will wind up |
- // in a process of that type, even if that means creating a new process. |
- void TestProcessOverflow() { |
- int tab_count = 1; |
- int host_count = 1; |
- WebContents* tab1 = NULL; |
- WebContents* tab2 = NULL; |
- content::RenderProcessHost* rph1 = NULL; |
- content::RenderProcessHost* rph2 = NULL; |
- content::RenderProcessHost* rph3 = NULL; |
- |
- // Change the first tab to be the omnibox page (TYPE_WEBUI). |
- GURL omnibox(chrome::kChromeUIOmniboxURL); |
- ui_test_utils::NavigateToURL(browser(), omnibox); |
- EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
- tab1 = browser()->tab_strip_model()->GetWebContentsAt(tab_count - 1); |
- rph1 = tab1->GetRenderProcessHost(); |
- EXPECT_EQ(omnibox, tab1->GetURL()); |
- EXPECT_EQ(host_count, RenderProcessHostCount()); |
- |
- // Create a new TYPE_TABBED tab. It should be in its own process. |
- GURL page1("data:text/html,hello world1"); |
- |
- ui_test_utils::WindowedTabAddedNotificationObserver observer1( |
- content::NotificationService::AllSources()); |
- chrome::ShowSingletonTab(browser(), page1); |
- observer1.Wait(); |
- |
- tab_count++; |
- host_count++; |
- EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
- tab1 = browser()->tab_strip_model()->GetWebContentsAt(tab_count - 1); |
- rph2 = tab1->GetRenderProcessHost(); |
- EXPECT_EQ(tab1->GetURL(), page1); |
- EXPECT_EQ(host_count, RenderProcessHostCount()); |
- EXPECT_NE(rph1, rph2); |
- |
- // Create another TYPE_TABBED tab. It should share the previous process. |
- GURL page2("data:text/html,hello world2"); |
- ui_test_utils::WindowedTabAddedNotificationObserver observer2( |
- content::NotificationService::AllSources()); |
- chrome::ShowSingletonTab(browser(), page2); |
- observer2.Wait(); |
- tab_count++; |
- EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
- tab2 = browser()->tab_strip_model()->GetWebContentsAt(tab_count - 1); |
- EXPECT_EQ(tab2->GetURL(), page2); |
- EXPECT_EQ(host_count, RenderProcessHostCount()); |
- EXPECT_EQ(tab2->GetRenderProcessHost(), rph2); |
- |
- // Create another TYPE_WEBUI tab. It should share the process with omnibox. |
- // Note: intentionally create this tab after the TYPE_TABBED tabs to |
- // exercise bug 43448 where extension and WebUI tabs could get combined into |
- // normal renderers. |
- GURL history(chrome::kChromeUIHistoryURL); |
- ui_test_utils::WindowedTabAddedNotificationObserver observer3( |
- content::NotificationService::AllSources()); |
- chrome::ShowSingletonTab(browser(), history); |
- observer3.Wait(); |
- tab_count++; |
- EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
- tab2 = browser()->tab_strip_model()->GetWebContentsAt(tab_count - 1); |
- EXPECT_EQ(tab2->GetURL(), GURL(history)); |
- EXPECT_EQ(host_count, RenderProcessHostCount()); |
- EXPECT_EQ(tab2->GetRenderProcessHost(), rph1); |
- |
- // Create a TYPE_EXTENSION tab. It should be in its own process. |
- // (the bookmark manager is implemented as an extension) |
- GURL bookmarks(chrome::kChromeUIBookmarksURL); |
- ui_test_utils::WindowedTabAddedNotificationObserver observer4( |
- content::NotificationService::AllSources()); |
- chrome::ShowSingletonTab(browser(), bookmarks); |
- observer4.Wait(); |
- tab_count++; |
- host_count++; |
- EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
- tab1 = browser()->tab_strip_model()->GetWebContentsAt(tab_count - 1); |
- rph3 = tab1->GetRenderProcessHost(); |
- EXPECT_EQ(tab1->GetURL(), bookmarks); |
- EXPECT_EQ(host_count, RenderProcessHostCount()); |
- EXPECT_NE(rph1, rph3); |
- EXPECT_NE(rph2, rph3); |
- } |
-}; |
- |
- |
-class ChromeRenderProcessHostTestWithCommandLine |
- : public ChromeRenderProcessHostTest { |
- protected: |
- void SetUpCommandLine(base::CommandLine* command_line) override { |
- command_line->AppendSwitchASCII(switches::kRendererProcessLimit, "1"); |
- } |
-}; |
- |
-// Disable on Mac and Windows due to ongoing flakiness. (crbug.com/442785) |
-#if defined(OS_MACOSX) || defined(OS_WIN) |
-#define MAYBE_ProcessPerTab DISABLED_ProcessPerTab |
-#else |
-#define MAYBE_ProcessPerTab ProcessPerTab |
-#endif |
- |
-IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, MAYBE_ProcessPerTab) { |
- // Set max renderers to 1 to force running out of processes. |
- content::RenderProcessHost::SetMaxRendererProcessCount(1); |
- |
- base::CommandLine& parsed_command_line = |
- *base::CommandLine::ForCurrentProcess(); |
- parsed_command_line.AppendSwitch(switches::kProcessPerTab); |
- |
- int tab_count = 1; |
- int host_count = 1; |
- |
- // Change the first tab to be the new tab page (TYPE_WEBUI). |
- GURL omnibox(chrome::kChromeUIOmniboxURL); |
- ui_test_utils::NavigateToURL(browser(), omnibox); |
- EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
- EXPECT_EQ(host_count, RenderProcessHostCount()); |
- |
- // Create a new TYPE_TABBED tab. It should be in its own process. |
- GURL page1("data:text/html,hello world1"); |
- ui_test_utils::WindowedTabAddedNotificationObserver observer1( |
- content::NotificationService::AllSources()); |
- chrome::ShowSingletonTab(browser(), page1); |
- observer1.Wait(); |
- tab_count++; |
- host_count++; |
- EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
- EXPECT_EQ(host_count, RenderProcessHostCount()); |
- |
- // Create another TYPE_TABBED tab. It should share the previous process. |
- GURL page2("data:text/html,hello world2"); |
- ui_test_utils::WindowedTabAddedNotificationObserver observer2( |
- content::NotificationService::AllSources()); |
- chrome::ShowSingletonTab(browser(), page2); |
- observer2.Wait(); |
- tab_count++; |
- EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
- EXPECT_EQ(host_count, RenderProcessHostCount()); |
- |
- // Create another omnibox tab. It should share the process with the other |
- // WebUI. |
- ui_test_utils::NavigateToURLWithDisposition( |
- browser(), omnibox, NEW_FOREGROUND_TAB, |
- ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); |
- tab_count++; |
- EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
- EXPECT_EQ(host_count, RenderProcessHostCount()); |
- |
- // Create another omnibox tab. It should share the process with the other |
- // WebUI. |
- ui_test_utils::NavigateToURLWithDisposition( |
- browser(), omnibox, NEW_FOREGROUND_TAB, |
- ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); |
- tab_count++; |
- EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
- EXPECT_EQ(host_count, RenderProcessHostCount()); |
-} |
- |
-// We don't change process priorities on Mac or Posix because the user lacks the |
-// permission to raise a process' priority even after lowering it. |
-#if defined(OS_WIN) || defined(OS_LINUX) |
-#if defined(OS_WIN) |
-// Flaky test: crbug.com/394368 |
-#define MAYBE_Backgrounding DISABLED_Backgrounding |
-#else |
-#define MAYBE_Backgrounding Backgrounding |
-#endif |
-IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, MAYBE_Backgrounding) { |
- if (!base::Process::CanBackgroundProcesses()) { |
- LOG(ERROR) << "Can't background processes"; |
- return; |
- } |
- base::CommandLine& parsed_command_line = |
- *base::CommandLine::ForCurrentProcess(); |
- parsed_command_line.AppendSwitch(switches::kProcessPerTab); |
- |
- // Change the first tab to be the omnibox page (TYPE_WEBUI). |
- GURL omnibox(chrome::kChromeUIOmniboxURL); |
- ui_test_utils::NavigateToURL(browser(), omnibox); |
- |
- // Create a new tab. It should be foreground. |
- GURL page1("data:text/html,hello world1"); |
- base::Process process1 = ShowSingletonTab(page1); |
- ASSERT_TRUE(process1.IsValid()); |
- EXPECT_FALSE(process1.IsProcessBackgrounded()); |
- |
- // Create another tab. It should be foreground, and the first tab should |
- // now be background. |
- GURL page2("data:text/html,hello world2"); |
- base::Process process2 = ShowSingletonTab(page2); |
- ASSERT_TRUE(process2.IsValid()); |
- EXPECT_NE(process1.Pid(), process2.Pid()); |
- EXPECT_TRUE(process1.IsProcessBackgrounded()); |
- EXPECT_FALSE(process2.IsProcessBackgrounded()); |
- |
- // Load another tab in background. The renderer of the new tab should be |
- // backgrounded, while visibility of the other renderers should not change. |
- GURL page3("data:text/html,hello world3"); |
- base::Process process3 = OpenBackgroundTab(page3); |
- ASSERT_TRUE(process3.IsValid()); |
- EXPECT_NE(process3.Pid(), process1.Pid()); |
- EXPECT_NE(process3.Pid(), process2.Pid()); |
- EXPECT_TRUE(process1.IsProcessBackgrounded()); |
- EXPECT_FALSE(process2.IsProcessBackgrounded()); |
- EXPECT_TRUE(process3.IsProcessBackgrounded()); |
- |
- // Navigate back to the first page. Its renderer should be in foreground |
- // again while the other renderers should be backgrounded. |
- EXPECT_EQ(process1.Pid(), ShowSingletonTab(page1).Pid()); |
- EXPECT_FALSE(process1.IsProcessBackgrounded()); |
- EXPECT_TRUE(process2.IsProcessBackgrounded()); |
- EXPECT_TRUE(process3.IsProcessBackgrounded()); |
-} |
-#endif |
- |
-// TODO(nasko): crbug.com/173137 |
-// Disable on Mac 10.9 due to ongoing flakiness. (crbug.com/442785) |
-#if defined(OS_WIN) || defined(OS_MACOSX) |
-#define MAYBE_ProcessOverflow DISABLED_ProcessOverflow |
-#else |
-#define MAYBE_ProcessOverflow ProcessOverflow |
-#endif |
- |
-IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, MAYBE_ProcessOverflow) { |
- // Set max renderers to 1 to force running out of processes. |
- content::RenderProcessHost::SetMaxRendererProcessCount(1); |
- TestProcessOverflow(); |
-} |
- |
-// Disable on Mac 10.9 due to ongoing flakiness. (crbug.com/442785) |
-#if defined(OS_MACOSX) || defined(OS_WIN) |
-#define MAYBE_ProcessOverflowCommandLine DISABLED_ProcessOverflow |
-#else |
-#define MAYBE_ProcessOverflowCommandLine ProcessOverflow |
-#endif |
- |
-// Variation of the ProcessOverflow test, which is driven through command line |
-// parameter instead of direct function call into the class. |
-IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTestWithCommandLine, |
- MAYBE_ProcessOverflowCommandLine) { |
- TestProcessOverflow(); |
-} |
- |
-// Ensure that DevTools opened to debug DevTools is launched in a separate |
-// process when --process-per-tab is set. See crbug.com/69873. |
-IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, |
- DevToolsOnSelfInOwnProcessPPT) { |
-#if defined(OS_WIN) && defined(USE_ASH) |
- // Disable this test in Metro+Ash for now (http://crbug.com/262796). |
- if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
- switches::kAshBrowserTests)) |
- return; |
-#endif |
- |
- base::CommandLine& parsed_command_line = |
- *base::CommandLine::ForCurrentProcess(); |
- parsed_command_line.AppendSwitch(switches::kProcessPerTab); |
- |
- int tab_count = 1; |
- int host_count = 1; |
- |
- GURL page1("data:text/html,hello world1"); |
- ui_test_utils::WindowedTabAddedNotificationObserver observer1( |
- content::NotificationService::AllSources()); |
- chrome::ShowSingletonTab(browser(), page1); |
- observer1.Wait(); |
- tab_count++; |
- host_count++; |
- EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
- EXPECT_EQ(host_count, RenderProcessHostCount()); |
- |
- // DevTools start in docked mode (no new tab), in a separate process. |
- chrome::ToggleDevToolsWindow(browser(), DevToolsToggleAction::Inspect()); |
- host_count++; |
- EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
- EXPECT_EQ(host_count, RenderProcessHostCount()); |
- |
- WebContents* devtools = FindFirstDevToolsContents(); |
- DCHECK(devtools); |
- |
- // DevTools start in a separate process. |
- DevToolsWindow::OpenDevToolsWindow(devtools, DevToolsToggleAction::Inspect()); |
- host_count++; |
- EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
- EXPECT_EQ(host_count, RenderProcessHostCount()); |
- |
- // close docked devtools |
- content::WindowedNotificationObserver close_observer( |
- content::NOTIFICATION_WEB_CONTENTS_DESTROYED, |
- content::Source<WebContents>(devtools)); |
- |
- chrome::ToggleDevToolsWindow(browser(), DevToolsToggleAction::Toggle()); |
- close_observer.Wait(); |
-} |
- |
-// Ensure that DevTools opened to debug DevTools is launched in a separate |
-// process. See crbug.com/69873. |
-IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, |
- DevToolsOnSelfInOwnProcess) { |
-#if defined(OS_WIN) && defined(USE_ASH) |
- // Disable this test in Metro+Ash for now (http://crbug.com/262796). |
- if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
- switches::kAshBrowserTests)) |
- return; |
-#endif |
- |
- int tab_count = 1; |
- int host_count = 1; |
- |
- GURL page1("data:text/html,hello world1"); |
- ui_test_utils::WindowedTabAddedNotificationObserver observer1( |
- content::NotificationService::AllSources()); |
- chrome::ShowSingletonTab(browser(), page1); |
- observer1.Wait(); |
- tab_count++; |
- host_count++; |
- EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
- EXPECT_EQ(host_count, RenderProcessHostCount()); |
- |
- // DevTools start in docked mode (no new tab), in a separate process. |
- chrome::ToggleDevToolsWindow(browser(), DevToolsToggleAction::Inspect()); |
- host_count++; |
- EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
- EXPECT_EQ(host_count, RenderProcessHostCount()); |
- |
- WebContents* devtools = FindFirstDevToolsContents(); |
- DCHECK(devtools); |
- |
- // DevTools start in a separate process. |
- DevToolsWindow::OpenDevToolsWindow(devtools, DevToolsToggleAction::Inspect()); |
- host_count++; |
- EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
- EXPECT_EQ(host_count, RenderProcessHostCount()); |
- |
- // close docked devtools |
- content::WindowedNotificationObserver close_observer( |
- content::NOTIFICATION_WEB_CONTENTS_DESTROYED, |
- content::Source<content::WebContents>(devtools)); |
- chrome::ToggleDevToolsWindow(browser(), DevToolsToggleAction::Toggle()); |
- close_observer.Wait(); |
-} |
- |
-// This class's goal is to close the browser window when a renderer process has |
-// crashed. It does so by monitoring WebContents for RenderProcessGone event and |
-// closing the passed in TabStripModel. This is used in the following test case. |
-class WindowDestroyer : public content::WebContentsObserver { |
- public: |
- WindowDestroyer(content::WebContents* web_contents, TabStripModel* model) |
- : content::WebContentsObserver(web_contents), |
- tab_strip_model_(model) { |
- } |
- |
- void RenderProcessGone(base::TerminationStatus status) override { |
- // Wait for the window to be destroyed, which will ensure all other |
- // RenderViewHost objects are deleted before we return and proceed with |
- // the next iteration of notifications. |
- content::WindowedNotificationObserver observer( |
- chrome::NOTIFICATION_BROWSER_CLOSED, |
- content::NotificationService::AllSources()); |
- tab_strip_model_->CloseAllTabs(); |
- observer.Wait(); |
- } |
- |
- private: |
- TabStripModel* tab_strip_model_; |
- |
- DISALLOW_COPY_AND_ASSIGN(WindowDestroyer); |
-}; |
- |
-// Test to ensure that while iterating through all listeners in |
-// RenderProcessHost and invalidating them, we remove them properly and don't |
-// access already freed objects. See http://crbug.com/255524. |
-IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, |
- CloseAllTabsDuringProcessDied) { |
- GURL url(chrome::kChromeUIOmniboxURL); |
- |
- ui_test_utils::NavigateToURL(browser(), url); |
- ui_test_utils::NavigateToURLWithDisposition( |
- browser(), url, NEW_BACKGROUND_TAB, |
- ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); |
- |
- EXPECT_EQ(2, browser()->tab_strip_model()->count()); |
- |
- WebContents* wc1 = browser()->tab_strip_model()->GetWebContentsAt(0); |
- WebContents* wc2 = browser()->tab_strip_model()->GetWebContentsAt(1); |
- EXPECT_EQ(wc1->GetRenderProcessHost(), wc2->GetRenderProcessHost()); |
- |
- // Create an object that will close the window on a process crash. |
- WindowDestroyer destroyer(wc1, browser()->tab_strip_model()); |
- |
- content::WindowedNotificationObserver observer( |
- chrome::NOTIFICATION_BROWSER_CLOSED, |
- content::NotificationService::AllSources()); |
- |
- // Kill the renderer process, simulating a crash. This should the ProcessDied |
- // method to be called. Alternatively, RenderProcessHost::OnChannelError can |
- // be called to directly force a call to ProcessDied. |
- wc1->GetRenderProcessHost()->Shutdown(-1, true); |
- |
- observer.Wait(); |
-} |
+// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "base/command_line.h" |
+#include "base/path_service.h" |
+#include "base/process/process.h" |
+#include "base/test/test_timeouts.h" |
+#include "chrome/browser/chrome_notification_types.h" |
+#include "chrome/browser/devtools/devtools_window.h" |
+#include "chrome/browser/search/search.h" |
+#include "chrome/browser/ui/browser.h" |
+#include "chrome/browser/ui/browser_commands.h" |
+#include "chrome/browser/ui/singleton_tabs.h" |
+#include "chrome/browser/ui/tabs/tab_strip_model.h" |
+#include "chrome/common/chrome_switches.h" |
+#include "chrome/common/url_constants.h" |
+#include "chrome/test/base/in_process_browser_test.h" |
+#include "chrome/test/base/test_switches.h" |
+#include "chrome/test/base/ui_test_utils.h" |
+#include "content/public/browser/notification_service.h" |
+#include "content/public/browser/render_process_host.h" |
+#include "content/public/browser/render_view_host.h" |
+#include "content/public/browser/render_widget_host_iterator.h" |
+#include "content/public/browser/web_contents.h" |
+#include "content/public/browser/web_contents_observer.h" |
+#include "content/public/test/browser_test_utils.h" |
+#include "net/base/filename_util.h" |
+#include "net/test/embedded_test_server/embedded_test_server.h" |
+ |
+using content::RenderViewHost; |
+using content::RenderWidgetHost; |
+using content::WebContents; |
+ |
+namespace { |
+ |
+int RenderProcessHostCount() { |
+ content::RenderProcessHost::iterator hosts = |
+ content::RenderProcessHost::AllHostsIterator(); |
+ int count = 0; |
+ while (!hosts.IsAtEnd()) { |
+ if (hosts.GetCurrentValue()->HasConnection()) |
+ count++; |
+ hosts.Advance(); |
+ } |
+ return count; |
+} |
+ |
+WebContents* FindFirstDevToolsContents() { |
+ scoped_ptr<content::RenderWidgetHostIterator> widgets( |
+ RenderWidgetHost::GetRenderWidgetHosts()); |
+ while (content::RenderWidgetHost* widget = widgets->GetNextHost()) { |
+ if (!widget->GetProcess()->HasConnection()) |
+ continue; |
+ if (!widget->IsRenderView()) |
+ continue; |
+ RenderViewHost* host = RenderViewHost::From(widget); |
+ WebContents* contents = WebContents::FromRenderViewHost(host); |
+ GURL url = contents->GetURL(); |
+ if (url.SchemeIs(content::kChromeDevToolsScheme)) |
+ return contents; |
+ } |
+ return NULL; |
+} |
+ |
+// TODO(rvargas) crbug.com/417532: Remove this code. |
+base::Process ProcessFromHandle(base::ProcessHandle handle) { |
+#if defined(OS_WIN) |
+ if (handle == GetCurrentProcess()) |
+ return base::Process::Current(); |
+ |
+ base::ProcessHandle out_handle; |
+ if (!::DuplicateHandle(GetCurrentProcess(), handle, GetCurrentProcess(), |
+ &out_handle, 0, FALSE, DUPLICATE_SAME_ACCESS)) { |
+ return base::Process(); |
+ } |
+ handle = out_handle; |
+#endif // defined(OS_WIN) |
+ return base::Process(handle); |
+} |
+ |
+} // namespace |
+ |
+class ChromeRenderProcessHostTest : public InProcessBrowserTest { |
+ public: |
+ ChromeRenderProcessHostTest() {} |
+ |
+ // Show a tab, activating the current one if there is one, and wait for |
+ // the renderer process to be created or foregrounded, returning the process |
+ // handle. |
+ base::Process ShowSingletonTab(const GURL& page) { |
+ chrome::ShowSingletonTab(browser(), page); |
+ WebContents* wc = browser()->tab_strip_model()->GetActiveWebContents(); |
+ CHECK(wc->GetURL() == page); |
+ |
+ WaitForLauncherThread(); |
+ WaitForMessageProcessing(wc); |
+ return ProcessFromHandle(wc->GetRenderProcessHost()->GetHandle()); |
+ } |
+ |
+ // Loads the given url in a new background tab and returns the handle of its |
+ // renderer. |
+ base::Process OpenBackgroundTab(const GURL& page) { |
+ ui_test_utils::NavigateToURLWithDisposition( |
+ browser(), page, NEW_BACKGROUND_TAB, |
+ ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); |
+ |
+ TabStripModel* tab_strip = browser()->tab_strip_model(); |
+ WebContents* wc = |
+ tab_strip->GetWebContentsAt(tab_strip->active_index() + 1); |
+ CHECK(wc->GetVisibleURL() == page); |
+ |
+ WaitForLauncherThread(); |
+ WaitForMessageProcessing(wc); |
+ return ProcessFromHandle(wc->GetRenderProcessHost()->GetHandle()); |
+ } |
+ |
+ // Ensures that the backgrounding / foregrounding gets a chance to run. |
+ void WaitForLauncherThread() { |
+ content::BrowserThread::PostTaskAndReply( |
+ content::BrowserThread::PROCESS_LAUNCHER, FROM_HERE, |
+ base::Bind(&base::DoNothing), base::MessageLoop::QuitClosure()); |
+ base::MessageLoop::current()->Run(); |
+ } |
+ |
+ // Implicitly waits for the renderer process associated with the specified |
+ // WebContents to process outstanding IPC messages by running some JavaScript |
+ // and waiting for the result. |
+ void WaitForMessageProcessing(WebContents* wc) { |
+ bool result = false; |
+ ASSERT_TRUE(content::ExecuteScriptAndExtractBool( |
+ wc, "window.domAutomationController.send(true);", &result)); |
+ ASSERT_TRUE(result); |
+ } |
+ |
+ // When we hit the max number of renderers, verify that the way we do process |
+ // sharing behaves correctly. In particular, this test is verifying that even |
+ // when we hit the max process limit, that renderers of each type will wind up |
+ // in a process of that type, even if that means creating a new process. |
+ void TestProcessOverflow() { |
+ int tab_count = 1; |
+ int host_count = 1; |
+ WebContents* tab1 = NULL; |
+ WebContents* tab2 = NULL; |
+ content::RenderProcessHost* rph1 = NULL; |
+ content::RenderProcessHost* rph2 = NULL; |
+ content::RenderProcessHost* rph3 = NULL; |
+ |
+ // Change the first tab to be the omnibox page (TYPE_WEBUI). |
+ GURL omnibox(chrome::kChromeUIOmniboxURL); |
+ ui_test_utils::NavigateToURL(browser(), omnibox); |
+ EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
+ tab1 = browser()->tab_strip_model()->GetWebContentsAt(tab_count - 1); |
+ rph1 = tab1->GetRenderProcessHost(); |
+ EXPECT_EQ(omnibox, tab1->GetURL()); |
+ EXPECT_EQ(host_count, RenderProcessHostCount()); |
+ |
+ // Create a new TYPE_TABBED tab. It should be in its own process. |
+ GURL page1("data:text/html,hello world1"); |
+ |
+ ui_test_utils::WindowedTabAddedNotificationObserver observer1( |
+ content::NotificationService::AllSources()); |
+ chrome::ShowSingletonTab(browser(), page1); |
+ observer1.Wait(); |
+ |
+ tab_count++; |
+ host_count++; |
+ EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
+ tab1 = browser()->tab_strip_model()->GetWebContentsAt(tab_count - 1); |
+ rph2 = tab1->GetRenderProcessHost(); |
+ EXPECT_EQ(tab1->GetURL(), page1); |
+ EXPECT_EQ(host_count, RenderProcessHostCount()); |
+ EXPECT_NE(rph1, rph2); |
+ |
+ // Create another TYPE_TABBED tab. It should share the previous process. |
+ GURL page2("data:text/html,hello world2"); |
+ ui_test_utils::WindowedTabAddedNotificationObserver observer2( |
+ content::NotificationService::AllSources()); |
+ chrome::ShowSingletonTab(browser(), page2); |
+ observer2.Wait(); |
+ tab_count++; |
+ EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
+ tab2 = browser()->tab_strip_model()->GetWebContentsAt(tab_count - 1); |
+ EXPECT_EQ(tab2->GetURL(), page2); |
+ EXPECT_EQ(host_count, RenderProcessHostCount()); |
+ EXPECT_EQ(tab2->GetRenderProcessHost(), rph2); |
+ |
+ // Create another TYPE_WEBUI tab. It should share the process with omnibox. |
+ // Note: intentionally create this tab after the TYPE_TABBED tabs to |
+ // exercise bug 43448 where extension and WebUI tabs could get combined into |
+ // normal renderers. |
+ GURL history(chrome::kChromeUIHistoryURL); |
+ ui_test_utils::WindowedTabAddedNotificationObserver observer3( |
+ content::NotificationService::AllSources()); |
+ chrome::ShowSingletonTab(browser(), history); |
+ observer3.Wait(); |
+ tab_count++; |
+ EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
+ tab2 = browser()->tab_strip_model()->GetWebContentsAt(tab_count - 1); |
+ EXPECT_EQ(tab2->GetURL(), GURL(history)); |
+ EXPECT_EQ(host_count, RenderProcessHostCount()); |
+ EXPECT_EQ(tab2->GetRenderProcessHost(), rph1); |
+ |
+ // Create a TYPE_EXTENSION tab. It should be in its own process. |
+ // (the bookmark manager is implemented as an extension) |
+ GURL bookmarks(chrome::kChromeUIBookmarksURL); |
+ ui_test_utils::WindowedTabAddedNotificationObserver observer4( |
+ content::NotificationService::AllSources()); |
+ chrome::ShowSingletonTab(browser(), bookmarks); |
+ observer4.Wait(); |
+ tab_count++; |
+ host_count++; |
+ EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
+ tab1 = browser()->tab_strip_model()->GetWebContentsAt(tab_count - 1); |
+ rph3 = tab1->GetRenderProcessHost(); |
+ EXPECT_EQ(tab1->GetURL(), bookmarks); |
+ EXPECT_EQ(host_count, RenderProcessHostCount()); |
+ EXPECT_NE(rph1, rph3); |
+ EXPECT_NE(rph2, rph3); |
+ } |
+}; |
+ |
+class ChromeRenderProcessHostTestWithCommandLine |
+ : public ChromeRenderProcessHostTest { |
+ protected: |
+ void SetUpCommandLine(base::CommandLine* command_line) override { |
+ command_line->AppendSwitchASCII(switches::kRendererProcessLimit, "1"); |
+ } |
+}; |
+ |
+// Disable on Mac and Windows due to ongoing flakiness. (crbug.com/442785) |
+#if defined(OS_MACOSX) || defined(OS_WIN) |
+#define MAYBE_ProcessPerTab DISABLED_ProcessPerTab |
+#else |
+#define MAYBE_ProcessPerTab ProcessPerTab |
+#endif |
+ |
+IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, MAYBE_ProcessPerTab) { |
+ // Set max renderers to 1 to force running out of processes. |
+ content::RenderProcessHost::SetMaxRendererProcessCount(1); |
+ |
+ base::CommandLine& parsed_command_line = |
+ *base::CommandLine::ForCurrentProcess(); |
+ parsed_command_line.AppendSwitch(switches::kProcessPerTab); |
+ |
+ int tab_count = 1; |
+ int host_count = 1; |
+ |
+ // Change the first tab to be the new tab page (TYPE_WEBUI). |
+ GURL omnibox(chrome::kChromeUIOmniboxURL); |
+ ui_test_utils::NavigateToURL(browser(), omnibox); |
+ EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
+ EXPECT_EQ(host_count, RenderProcessHostCount()); |
+ |
+ // Create a new TYPE_TABBED tab. It should be in its own process. |
+ GURL page1("data:text/html,hello world1"); |
+ ui_test_utils::WindowedTabAddedNotificationObserver observer1( |
+ content::NotificationService::AllSources()); |
+ chrome::ShowSingletonTab(browser(), page1); |
+ observer1.Wait(); |
+ tab_count++; |
+ host_count++; |
+ EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
+ EXPECT_EQ(host_count, RenderProcessHostCount()); |
+ |
+ // Create another TYPE_TABBED tab. It should share the previous process. |
+ GURL page2("data:text/html,hello world2"); |
+ ui_test_utils::WindowedTabAddedNotificationObserver observer2( |
+ content::NotificationService::AllSources()); |
+ chrome::ShowSingletonTab(browser(), page2); |
+ observer2.Wait(); |
+ tab_count++; |
+ EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
+ EXPECT_EQ(host_count, RenderProcessHostCount()); |
+ |
+ // Create another omnibox tab. It should share the process with the other |
+ // WebUI. |
+ ui_test_utils::NavigateToURLWithDisposition( |
+ browser(), omnibox, NEW_FOREGROUND_TAB, |
+ ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); |
+ tab_count++; |
+ EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
+ EXPECT_EQ(host_count, RenderProcessHostCount()); |
+ |
+ // Create another omnibox tab. It should share the process with the other |
+ // WebUI. |
+ ui_test_utils::NavigateToURLWithDisposition( |
+ browser(), omnibox, NEW_FOREGROUND_TAB, |
+ ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); |
+ tab_count++; |
+ EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
+ EXPECT_EQ(host_count, RenderProcessHostCount()); |
+} |
+ |
+// We don't change process priorities on Mac or Posix because the user lacks the |
+// permission to raise a process' priority even after lowering it. |
+#if defined(OS_WIN) || defined(OS_LINUX) |
+#if defined(OS_WIN) |
+// Flaky test: crbug.com/394368 |
+#define MAYBE_Backgrounding DISABLED_Backgrounding |
+#else |
+#define MAYBE_Backgrounding Backgrounding |
+#endif |
+IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, MAYBE_Backgrounding) { |
+ if (!base::Process::CanBackgroundProcesses()) { |
+ LOG(ERROR) << "Can't background processes"; |
+ return; |
+ } |
+ base::CommandLine& parsed_command_line = |
+ *base::CommandLine::ForCurrentProcess(); |
+ parsed_command_line.AppendSwitch(switches::kProcessPerTab); |
+ |
+ // Change the first tab to be the omnibox page (TYPE_WEBUI). |
+ GURL omnibox(chrome::kChromeUIOmniboxURL); |
+ ui_test_utils::NavigateToURL(browser(), omnibox); |
+ |
+ // Create a new tab. It should be foreground. |
+ GURL page1("data:text/html,hello world1"); |
+ base::Process process1 = ShowSingletonTab(page1); |
+ ASSERT_TRUE(process1.IsValid()); |
+ EXPECT_FALSE(process1.IsProcessBackgrounded()); |
+ |
+ // Create another tab. It should be foreground, and the first tab should |
+ // now be background. |
+ GURL page2("data:text/html,hello world2"); |
+ base::Process process2 = ShowSingletonTab(page2); |
+ ASSERT_TRUE(process2.IsValid()); |
+ EXPECT_NE(process1.Pid(), process2.Pid()); |
+ EXPECT_TRUE(process1.IsProcessBackgrounded()); |
+ EXPECT_FALSE(process2.IsProcessBackgrounded()); |
+ |
+ // Load another tab in background. The renderer of the new tab should be |
+ // backgrounded, while visibility of the other renderers should not change. |
+ GURL page3("data:text/html,hello world3"); |
+ base::Process process3 = OpenBackgroundTab(page3); |
+ ASSERT_TRUE(process3.IsValid()); |
+ EXPECT_NE(process3.Pid(), process1.Pid()); |
+ EXPECT_NE(process3.Pid(), process2.Pid()); |
+ EXPECT_TRUE(process1.IsProcessBackgrounded()); |
+ EXPECT_FALSE(process2.IsProcessBackgrounded()); |
+ EXPECT_TRUE(process3.IsProcessBackgrounded()); |
+ |
+ // Navigate back to the first page. Its renderer should be in foreground |
+ // again while the other renderers should be backgrounded. |
+ EXPECT_EQ(process1.Pid(), ShowSingletonTab(page1).Pid()); |
+ EXPECT_FALSE(process1.IsProcessBackgrounded()); |
+ EXPECT_TRUE(process2.IsProcessBackgrounded()); |
+ EXPECT_TRUE(process3.IsProcessBackgrounded()); |
+} |
+#endif |
+ |
+// TODO(nasko): crbug.com/173137 |
+// Disable on Mac 10.9 due to ongoing flakiness. (crbug.com/442785) |
+#if defined(OS_WIN) || defined(OS_MACOSX) |
+#define MAYBE_ProcessOverflow DISABLED_ProcessOverflow |
+#else |
+#define MAYBE_ProcessOverflow ProcessOverflow |
+#endif |
+ |
+IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, MAYBE_ProcessOverflow) { |
+ // Set max renderers to 1 to force running out of processes. |
+ content::RenderProcessHost::SetMaxRendererProcessCount(1); |
+ TestProcessOverflow(); |
+} |
+ |
+// Disable on Mac 10.9 due to ongoing flakiness. (crbug.com/442785) |
+#if defined(OS_MACOSX) || defined(OS_WIN) |
+#define MAYBE_ProcessOverflowCommandLine DISABLED_ProcessOverflow |
+#else |
+#define MAYBE_ProcessOverflowCommandLine ProcessOverflow |
+#endif |
+ |
+// Variation of the ProcessOverflow test, which is driven through command line |
+// parameter instead of direct function call into the class. |
+IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTestWithCommandLine, |
+ MAYBE_ProcessOverflowCommandLine) { |
+ TestProcessOverflow(); |
+} |
+ |
+// Ensure that DevTools opened to debug DevTools is launched in a separate |
+// process when --process-per-tab is set. See crbug.com/69873. |
+IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, |
+ DevToolsOnSelfInOwnProcessPPT) { |
+#if defined(OS_WIN) && defined(USE_ASH) |
+ // Disable this test in Metro+Ash for now (http://crbug.com/262796). |
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
+ switches::kAshBrowserTests)) |
+ return; |
+#endif |
+ |
+ base::CommandLine& parsed_command_line = |
+ *base::CommandLine::ForCurrentProcess(); |
+ parsed_command_line.AppendSwitch(switches::kProcessPerTab); |
+ |
+ int tab_count = 1; |
+ int host_count = 1; |
+ |
+ GURL page1("data:text/html,hello world1"); |
+ ui_test_utils::WindowedTabAddedNotificationObserver observer1( |
+ content::NotificationService::AllSources()); |
+ chrome::ShowSingletonTab(browser(), page1); |
+ observer1.Wait(); |
+ tab_count++; |
+ host_count++; |
+ EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
+ EXPECT_EQ(host_count, RenderProcessHostCount()); |
+ |
+ // DevTools start in docked mode (no new tab), in a separate process. |
+ chrome::ToggleDevToolsWindow(browser(), DevToolsToggleAction::Inspect()); |
+ host_count++; |
+ EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
+ EXPECT_EQ(host_count, RenderProcessHostCount()); |
+ |
+ WebContents* devtools = FindFirstDevToolsContents(); |
+ DCHECK(devtools); |
+ |
+ // DevTools start in a separate process. |
+ DevToolsWindow::OpenDevToolsWindow(devtools, DevToolsToggleAction::Inspect()); |
+ host_count++; |
+ EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
+ EXPECT_EQ(host_count, RenderProcessHostCount()); |
+ |
+ // close docked devtools |
+ content::WindowedNotificationObserver close_observer( |
+ content::NOTIFICATION_WEB_CONTENTS_DESTROYED, |
+ content::Source<WebContents>(devtools)); |
+ |
+ chrome::ToggleDevToolsWindow(browser(), DevToolsToggleAction::Toggle()); |
+ close_observer.Wait(); |
+} |
+ |
+// Ensure that DevTools opened to debug DevTools is launched in a separate |
+// process. See crbug.com/69873. |
+IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, |
+ DevToolsOnSelfInOwnProcess) { |
+#if defined(OS_WIN) && defined(USE_ASH) |
+ // Disable this test in Metro+Ash for now (http://crbug.com/262796). |
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
+ switches::kAshBrowserTests)) |
+ return; |
+#endif |
+ |
+ int tab_count = 1; |
+ int host_count = 1; |
+ |
+ GURL page1("data:text/html,hello world1"); |
+ ui_test_utils::WindowedTabAddedNotificationObserver observer1( |
+ content::NotificationService::AllSources()); |
+ chrome::ShowSingletonTab(browser(), page1); |
+ observer1.Wait(); |
+ tab_count++; |
+ host_count++; |
+ EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
+ EXPECT_EQ(host_count, RenderProcessHostCount()); |
+ |
+ // DevTools start in docked mode (no new tab), in a separate process. |
+ chrome::ToggleDevToolsWindow(browser(), DevToolsToggleAction::Inspect()); |
+ host_count++; |
+ EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
+ EXPECT_EQ(host_count, RenderProcessHostCount()); |
+ |
+ WebContents* devtools = FindFirstDevToolsContents(); |
+ DCHECK(devtools); |
+ |
+ // DevTools start in a separate process. |
+ DevToolsWindow::OpenDevToolsWindow(devtools, DevToolsToggleAction::Inspect()); |
+ host_count++; |
+ EXPECT_EQ(tab_count, browser()->tab_strip_model()->count()); |
+ EXPECT_EQ(host_count, RenderProcessHostCount()); |
+ |
+ // close docked devtools |
+ content::WindowedNotificationObserver close_observer( |
+ content::NOTIFICATION_WEB_CONTENTS_DESTROYED, |
+ content::Source<content::WebContents>(devtools)); |
+ chrome::ToggleDevToolsWindow(browser(), DevToolsToggleAction::Toggle()); |
+ close_observer.Wait(); |
+} |
+ |
+// This class's goal is to close the browser window when a renderer process has |
+// crashed. It does so by monitoring WebContents for RenderProcessGone event and |
+// closing the passed in TabStripModel. This is used in the following test case. |
+class WindowDestroyer : public content::WebContentsObserver { |
+ public: |
+ WindowDestroyer(content::WebContents* web_contents, TabStripModel* model) |
+ : content::WebContentsObserver(web_contents), tab_strip_model_(model) {} |
+ |
+ void RenderProcessGone(base::TerminationStatus status) override { |
+ // Wait for the window to be destroyed, which will ensure all other |
+ // RenderViewHost objects are deleted before we return and proceed with |
+ // the next iteration of notifications. |
+ content::WindowedNotificationObserver observer( |
+ chrome::NOTIFICATION_BROWSER_CLOSED, |
+ content::NotificationService::AllSources()); |
+ tab_strip_model_->CloseAllTabs(); |
+ observer.Wait(); |
+ } |
+ |
+ private: |
+ TabStripModel* tab_strip_model_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(WindowDestroyer); |
+}; |
+ |
+// Test to ensure that while iterating through all listeners in |
+// RenderProcessHost and invalidating them, we remove them properly and don't |
+// access already freed objects. See http://crbug.com/255524. |
+IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostTest, |
+ CloseAllTabsDuringProcessDied) { |
+ GURL url(chrome::kChromeUIOmniboxURL); |
+ |
+ ui_test_utils::NavigateToURL(browser(), url); |
+ ui_test_utils::NavigateToURLWithDisposition( |
+ browser(), url, NEW_BACKGROUND_TAB, |
+ ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); |
+ |
+ EXPECT_EQ(2, browser()->tab_strip_model()->count()); |
+ |
+ WebContents* wc1 = browser()->tab_strip_model()->GetWebContentsAt(0); |
+ WebContents* wc2 = browser()->tab_strip_model()->GetWebContentsAt(1); |
+ EXPECT_EQ(wc1->GetRenderProcessHost(), wc2->GetRenderProcessHost()); |
+ |
+ // Create an object that will close the window on a process crash. |
+ WindowDestroyer destroyer(wc1, browser()->tab_strip_model()); |
+ |
+ content::WindowedNotificationObserver observer( |
+ chrome::NOTIFICATION_BROWSER_CLOSED, |
+ content::NotificationService::AllSources()); |
+ |
+ // Kill the renderer process, simulating a crash. This should the ProcessDied |
+ // method to be called. Alternatively, RenderProcessHost::OnChannelError can |
+ // be called to directly force a call to ProcessDied. |
+ wc1->GetRenderProcessHost()->Shutdown(-1, true); |
+ |
+ observer.Wait(); |
+} |
+ |
+// Sets up the browser in order to start the tests with two tabs open: one |
+// called "no audio" in foreground and another called "audio" in background with |
+// audio in playing state. Also sets up the variables containing the process |
+// associated with each tab, the urls of the two pages and the WebContents of |
+// the "audio" page. |
+class ChromeRenderProcessHostBackgroundingTest |
+ : public ChromeRenderProcessHostTest { |
+ public: |
+ ChromeRenderProcessHostBackgroundingTest() {} |
+ |
+ void SetUpCommandLine(base::CommandLine* command_line) override { |
+ command_line->AppendSwitch(switches::kProcessPerTab); |
+ } |
+ |
+ void SetUpOnMainThread() override { |
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); |
+ |
+ // Set up the server and get the test pages. |
+ base::FilePath test_data_dir; |
+ ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &test_data_dir)); |
+ embedded_test_server()->ServeFilesFromDirectory( |
+ test_data_dir.AppendASCII("chrome/test/data/")); |
+ audio_url_ = embedded_test_server()->GetURL("/extensions/loop_audio.html"); |
+ no_audio_url_ = embedded_test_server()->GetURL("/title1.html"); |
+ |
+ // Open a browser, navigate to the audio page and get its WebContents. |
+ ui_test_utils::NavigateToURL(browser(), audio_url_); |
+ audio_tab_web_contents_ = |
+ browser()->tab_strip_model()->GetActiveWebContents(); |
+ |
+ // Create a new tab for the no audio page and confirm that the process of |
+ // each tab is different and that both are valid. |
+ audio_process_ = ProcessFromHandle( |
+ audio_tab_web_contents_->GetRenderProcessHost()->GetHandle()); |
+ no_audio_process_ = ShowSingletonTab(no_audio_url_); |
+ ASSERT_NE(audio_process_.Pid(), no_audio_process_.Pid()); |
+ ASSERT_TRUE(no_audio_process_.IsValid()); |
+ ASSERT_TRUE(audio_process_.IsValid()); |
+ } |
+ |
+ protected: |
+ GURL audio_url_; |
+ GURL no_audio_url_; |
+ |
+ base::Process audio_process_; |
+ base::Process no_audio_process_; |
+ |
+ content::WebContents* audio_tab_web_contents_; |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(ChromeRenderProcessHostBackgroundingTest); |
+}; |
+ |
+// Test to make sure that a process is backgrounded when the audio stops playing |
+// from the active tab and there is an immediate tab switch. |
+IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostBackgroundingTest, |
+ ProcessPriorityAfterStoppedAudio) { |
+ // This test is invalid on platforms that can't background. |
+ if (!base::Process::CanBackgroundProcesses()) |
+ return; |
+ |
+ ShowSingletonTab(audio_url_); |
+ |
+ // Wait until the no audio page is backgrounded and the audio page is not |
+ // backgrounded. |
+ while (!no_audio_process_.IsProcessBackgrounded() || |
+ audio_process_.IsProcessBackgrounded()) { |
+ base::RunLoop().RunUntilIdle(); |
+ base::PlatformThread::Sleep(TestTimeouts::tiny_timeout()); |
+ } |
+ |
+ // Pause the audio and immediately switch to the no audio tab. |
+ ASSERT_TRUE(content::ExecuteScript( |
+ audio_tab_web_contents_, |
+ "document.getElementById('audioPlayer').pause();")); |
+ ShowSingletonTab(no_audio_url_); |
+ |
+ // Wait until the no audio page is not backgrounded and the audio page is |
+ // backgrounded. |
+ while (no_audio_process_.IsProcessBackgrounded() || |
+ !audio_process_.IsProcessBackgrounded()) { |
+ base::RunLoop().RunUntilIdle(); |
+ base::PlatformThread::Sleep(TestTimeouts::tiny_timeout()); |
+ } |
+} |
+ |
+// Test to make sure that a process is backgrounded automatically when audio |
+// stops playing from a hidden tab. |
+IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostBackgroundingTest, |
+ ProcessPriorityAfterAudioStopsOnNotVisibleTab) { |
+ // This test is invalid on platforms that can't background. |
+ if (!base::Process::CanBackgroundProcesses()) |
+ return; |
+ |
+ // Wait until the two pages are not backgrounded. |
+ while (no_audio_process_.IsProcessBackgrounded() || |
+ audio_process_.IsProcessBackgrounded()) { |
+ base::RunLoop().RunUntilIdle(); |
+ base::PlatformThread::Sleep(TestTimeouts::tiny_timeout()); |
+ } |
+ |
+ // Stop the audio. |
+ ASSERT_TRUE(content::ExecuteScript( |
+ audio_tab_web_contents_, |
+ "document.getElementById('audioPlayer').pause();")); |
+ |
+ // Wait until the no audio page is not backgrounded and the audio page is |
+ // backgrounded. |
+ while (no_audio_process_.IsProcessBackgrounded() || |
+ !audio_process_.IsProcessBackgrounded()) { |
+ base::RunLoop().RunUntilIdle(); |
+ base::PlatformThread::Sleep(TestTimeouts::tiny_timeout()); |
+ } |
+} |
+ |
+// Test to make sure that a process is un-backgrounded automatically when audio |
+// starts playing from a backgrounded tab. |
+IN_PROC_BROWSER_TEST_F(ChromeRenderProcessHostBackgroundingTest, |
+ ProcessPriorityAfterAudioStartsFromBackgroundTab) { |
+ // This test is invalid on platforms that can't background. |
+ if (!base::Process::CanBackgroundProcesses()) |
+ return; |
+ |
+ // Stop the audio. |
+ ASSERT_TRUE(content::ExecuteScript( |
+ audio_tab_web_contents_, |
+ "document.getElementById('audioPlayer').pause();")); |
+ |
+ // Wait until the no audio page is not backgrounded and the audio page is |
+ // backgrounded. |
+ while (no_audio_process_.IsProcessBackgrounded() || |
+ !audio_process_.IsProcessBackgrounded()) { |
+ base::RunLoop().RunUntilIdle(); |
+ base::PlatformThread::Sleep(TestTimeouts::tiny_timeout()); |
+ } |
+ |
+ // Start the audio from the backgrounded tab. |
+ ASSERT_TRUE( |
+ content::ExecuteScript(audio_tab_web_contents_, |
+ "document.getElementById('audioPlayer').play();")); |
+ |
+ // Wait until the two pages are not backgrounded. |
+ while (no_audio_process_.IsProcessBackgrounded() || |
+ audio_process_.IsProcessBackgrounded()) { |
+ base::RunLoop().RunUntilIdle(); |
+ base::PlatformThread::Sleep(TestTimeouts::tiny_timeout()); |
+ } |
+} |