diff options
author | nasko@chromium.org <nasko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-22 18:56:06 +0000 |
---|---|---|
committer | nasko@chromium.org <nasko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-22 18:56:06 +0000 |
commit | 76411f41ebe3a918f3108af85646d6e1fca604e8 (patch) | |
tree | d5510d46abd196864ab533253f5f7817a81a0fa4 | |
parent | b37db0786972fdd10c2977576a5db3c625215ceb (diff) | |
download | chromium_src-76411f41ebe3a918f3108af85646d6e1fca604e8.zip chromium_src-76411f41ebe3a918f3108af85646d6e1fca604e8.tar.gz chromium_src-76411f41ebe3a918f3108af85646d6e1fca604e8.tar.bz2 |
Fix bug where a large number of extensions can starve the browser for renderer processes
BUG=98737
TEST=Install multiple extensions, run Chrome with --render-process-limit=6. All extensions should be placed in only 2 processes.
Review URL: http://codereview.chromium.org/9418010
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@123072 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/chrome_content_browser_client.cc | 55 | ||||
-rw-r--r-- | chrome/browser/chrome_content_browser_client.h | 2 | ||||
-rw-r--r-- | chrome/browser/extensions/isolated_app_browsertest.cc | 132 | ||||
-rw-r--r-- | chrome/browser/extensions/process_management_browsertest.cc | 232 | ||||
-rw-r--r-- | chrome/browser/prerender/prerender_manager.cc | 3 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 1 | ||||
-rw-r--r-- | chrome/common/chrome_constants.cc | 2 | ||||
-rw-r--r-- | chrome/common/chrome_constants.h | 7 | ||||
-rw-r--r-- | content/browser/mock_content_browser_client.cc | 5 | ||||
-rw-r--r-- | content/browser/mock_content_browser_client.h | 2 | ||||
-rw-r--r-- | content/browser/renderer_host/render_process_host_impl.cc | 13 | ||||
-rw-r--r-- | content/browser/site_instance_impl.cc | 3 | ||||
-rw-r--r-- | content/public/browser/content_browser_client.h | 6 | ||||
-rw-r--r-- | content/public/browser/render_process_host.h | 3 | ||||
-rw-r--r-- | content/shell/shell_content_browser_client.cc | 5 | ||||
-rw-r--r-- | content/shell/shell_content_browser_client.h | 2 |
16 files changed, 334 insertions, 139 deletions
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index dc3625f..3c36f12 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc @@ -25,7 +25,9 @@ #include "chrome/browser/content_settings/tab_specific_content_settings.h" #include "chrome/browser/download/download_util.h" #include "chrome/browser/extensions/api/webrequest/webrequest_api.h" +#include "chrome/browser/extensions/extension_host.h" #include "chrome/browser/extensions/extension_info_map.h" +#include "chrome/browser/extensions/extension_process_manager.h" #include "chrome/browser/extensions/extension_message_handler.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_web_ui.h" @@ -44,6 +46,7 @@ #include "chrome/browser/printing/printing_message_filter.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_io_data.h" +#include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/renderer_host/chrome_render_message_filter.h" #include "chrome/browser/renderer_host/chrome_render_view_host_observer.h" #include "chrome/browser/renderer_host/plugin_info_message_filter.h" @@ -508,6 +511,58 @@ bool ChromeContentBrowserClient::IsSuitableHost( privilege_required; } +// This function is trying to limit the amount of processes used by extensions +// with background pages. It uses a globally set percentage of processes to +// run such extensions and if the limit is exceeded, it returns true, to +// indicate to the content module to group extensions together. +bool ChromeContentBrowserClient::ShouldTryToUseExistingProcessHost( + content::BrowserContext* browser_context, const GURL& url) { + // It has to be a valid URL for us to check for an extension. + if (!url.is_valid()) + return false; + + Profile* profile = Profile::FromBrowserContext(browser_context); + ExtensionService* service = profile->GetExtensionService(); + if (!service) + return false; + + // We have to have a valid extension with background page to proceed. + const Extension* extension = + service->extensions()->GetExtensionOrAppByURL(ExtensionURLInfo(url)); + if (!extension) + return false; + if (!extension->has_background_page()) + return false; + + std::set<int> process_ids; + size_t max_process_count = + content::RenderProcessHost::GetMaxRendererProcessCount(); + + // Go through all profiles to ensure we have total count of extension + // processes containing background pages, otherwise one profile can + // starve the other. + std::vector<Profile*> profiles = g_browser_process->profile_manager()-> + GetLoadedProfiles(); + for (size_t i = 0; i < profiles.size(); ++i) { + ExtensionProcessManager* epm = profiles[i]->GetExtensionProcessManager(); + for (ExtensionProcessManager::const_iterator iter = epm->begin(); + iter != epm->end(); + ++iter) { + ExtensionHost* host = *iter; + if (host->extension()->has_background_page()) { + process_ids.insert(host->render_process_host()->GetID()); + } + } + } + + if (process_ids.size() > + (max_process_count * chrome::kMaxShareOfExtensionProcesses)) { + return true; + } + + return false; +} + void ChromeContentBrowserClient::SiteInstanceGotProcess( SiteInstance* site_instance) { CHECK(site_instance->HasProcess()); diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h index 56ad007..8cc56ef 100644 --- a/chrome/browser/chrome_content_browser_client.h +++ b/chrome/browser/chrome_content_browser_client.h @@ -34,6 +34,8 @@ class ChromeContentBrowserClient : public content::ContentBrowserClient { virtual bool IsHandledURL(const GURL& url) OVERRIDE; virtual bool IsSuitableHost(content::RenderProcessHost* process_host, const GURL& url) OVERRIDE; + virtual bool ShouldTryToUseExistingProcessHost( + content::BrowserContext* browser_context, const GURL& url) OVERRIDE; virtual void SiteInstanceGotProcess( content::SiteInstance* site_instance) OVERRIDE; virtual void SiteInstanceDeleting(content::SiteInstance* site_instance) diff --git a/chrome/browser/extensions/isolated_app_browsertest.cc b/chrome/browser/extensions/isolated_app_browsertest.cc index 0c17507..26f7952 100644 --- a/chrome/browser/extensions/isolated_app_browsertest.cc +++ b/chrome/browser/extensions/isolated_app_browsertest.cc @@ -169,135 +169,3 @@ IN_PROC_BROWSER_TEST_F(IsolatedAppTest, NoCookieIsolationWithoutApp) { EXPECT_TRUE(HasCookie(browser()->GetWebContentsAt(2), "app2=4")); EXPECT_TRUE(HasCookie(browser()->GetWebContentsAt(2), "nonAppFrame=6")); } - -// Ensure that an isolated app never shares a process with WebUIs, non-isolated -// extensions, and normal webpages. None of these should ever comingle -// RenderProcessHosts even if we hit the process limit. -IN_PROC_BROWSER_TEST_F(IsolatedAppTest, ProcessOverflow) { - // Set max renderers to 1 to force running out of processes. - content::RenderProcessHost::SetMaxRendererProcessCount(1); - - host_resolver()->AddRule("*", "127.0.0.1"); - ASSERT_TRUE(test_server()->Start()); - - ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("isolated_apps/app1"))); - ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("isolated_apps/app2"))); - ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("hosted_app"))); - ASSERT_TRUE( - LoadExtension(test_data_dir_.AppendASCII("api_test/app_process"))); - - // The app under test acts on URLs whose host is "localhost", - // so the URLs we navigate to must have host "localhost". - GURL base_url = test_server()->GetURL( - "files/extensions/"); - GURL::Replacements replace_host; - std::string host_str("localhost"); // Must stay in scope with replace_host. - replace_host.SetHostStr(host_str); - base_url = base_url.ReplaceComponents(replace_host); - - // Load an extension before adding tabs. - const Extension* extension1 = LoadExtension( - test_data_dir_.AppendASCII("api_test/browser_action/basics")); - ASSERT_TRUE(extension1); - GURL extension1_url = extension1->url(); - - // Create multiple tabs for each type of renderer that might exist. - ui_test_utils::NavigateToURLWithDisposition( - browser(), base_url.Resolve("isolated_apps/app1/main.html"), - CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); - ui_test_utils::NavigateToURLWithDisposition( - browser(), GURL(chrome::kChromeUINewTabURL), - NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); - ui_test_utils::NavigateToURLWithDisposition( - browser(), base_url.Resolve("hosted_app/main.html"), - NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); - ui_test_utils::NavigateToURLWithDisposition( - browser(), base_url.Resolve("test_file.html"), - NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); - - ui_test_utils::NavigateToURLWithDisposition( - browser(), base_url.Resolve("isolated_apps/app2/main.html"), - NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); - ui_test_utils::NavigateToURLWithDisposition( - browser(), GURL(chrome::kChromeUINewTabURL), - NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); - ui_test_utils::NavigateToURLWithDisposition( - browser(), base_url.Resolve("api_test/app_process/path1/empty.html"), - NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); - ui_test_utils::NavigateToURLWithDisposition( - browser(), base_url.Resolve("test_file_with_body.html"), - NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); - - // Load another copy of isolated app 1. - ui_test_utils::NavigateToURLWithDisposition( - browser(), base_url.Resolve("isolated_apps/app1/main.html"), - NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); - - // Load another extension. - const Extension* extension2 = LoadExtension( - test_data_dir_.AppendASCII("api_test/browser_action/close_background")); - ASSERT_TRUE(extension2); - GURL extension2_url = extension2->url(); - - // Get tab processes. - ASSERT_EQ(9, browser()->tab_count()); - content::RenderProcessHost* isolated1_host = - browser()->GetWebContentsAt(0)->GetRenderProcessHost(); - content::RenderProcessHost* ntp1_host = - browser()->GetWebContentsAt(1)->GetRenderProcessHost(); - content::RenderProcessHost* hosted1_host = - browser()->GetWebContentsAt(2)->GetRenderProcessHost(); - content::RenderProcessHost* web1_host = - browser()->GetWebContentsAt(3)->GetRenderProcessHost(); - - content::RenderProcessHost* isolated2_host = - browser()->GetWebContentsAt(4)->GetRenderProcessHost(); - content::RenderProcessHost* ntp2_host = - browser()->GetWebContentsAt(5)->GetRenderProcessHost(); - content::RenderProcessHost* hosted2_host = - browser()->GetWebContentsAt(6)->GetRenderProcessHost(); - content::RenderProcessHost* web2_host = - browser()->GetWebContentsAt(7)->GetRenderProcessHost(); - - content::RenderProcessHost* second_isolated1_host = - browser()->GetWebContentsAt(8)->GetRenderProcessHost(); - - // Get extension processes. - ExtensionProcessManager* process_manager = - browser()->GetProfile()->GetExtensionProcessManager(); - content::RenderProcessHost* extension1_host = - process_manager->GetSiteInstanceForURL(extension1_url)->GetProcess(); - content::RenderProcessHost* extension2_host = - process_manager->GetSiteInstanceForURL(extension2_url)->GetProcess(); - - // An isolated app only shares with other instances of itself, not other - // isolated apps or anything else. - EXPECT_EQ(isolated1_host, second_isolated1_host); - EXPECT_NE(isolated1_host, isolated2_host); - EXPECT_NE(isolated1_host, ntp1_host); - EXPECT_NE(isolated1_host, hosted1_host); - EXPECT_NE(isolated1_host, web1_host); - EXPECT_NE(isolated1_host, extension1_host); - EXPECT_NE(isolated2_host, ntp1_host); - EXPECT_NE(isolated2_host, hosted1_host); - EXPECT_NE(isolated2_host, web1_host); - EXPECT_NE(isolated2_host, extension1_host); - - // Everything else is clannish. WebUI only shares with other WebUI. - EXPECT_EQ(ntp1_host, ntp2_host); - EXPECT_NE(ntp1_host, hosted1_host); - EXPECT_NE(ntp1_host, web1_host); - EXPECT_NE(ntp1_host, extension1_host); - - // Hosted apps only share with each other. - EXPECT_EQ(hosted1_host, hosted2_host); - EXPECT_NE(hosted1_host, web1_host); - EXPECT_NE(hosted1_host, extension1_host); - - // Web pages only share with each other. - EXPECT_EQ(web1_host, web2_host); - EXPECT_NE(web1_host, extension1_host); - - // Extensions only share with each other. - EXPECT_EQ(extension1_host, extension2_host); -} diff --git a/chrome/browser/extensions/process_management_browsertest.cc b/chrome/browser/extensions/process_management_browsertest.cc new file mode 100644 index 0000000..54022c6 --- /dev/null +++ b/chrome/browser/extensions/process_management_browsertest.cc @@ -0,0 +1,232 @@ +// 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/utf_string_conversions.h" +#include "chrome/browser/automation/automation_util.h" +#include "chrome/browser/extensions/extension_apitest.h" +#include "chrome/browser/extensions/extension_host.h" +#include "chrome/browser/extensions/extension_process_manager.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/url_constants.h" +#include "chrome/test/base/ui_test_utils.h" +#include "content/browser/renderer_host/render_view_host.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/site_instance.h" +#include "content/public/browser/web_contents.h" +#include "net/base/mock_host_resolver.h" + +using content::NavigationController; +using content::WebContents; + +namespace { + +class ProcessManagementTest : public ExtensionBrowserTest { + private: + // This is needed for testing isolated apps, which are still experimental. + virtual void SetUpCommandLine(CommandLine* command_line) { + ExtensionBrowserTest::SetUpCommandLine(command_line); + command_line->AppendSwitch(switches::kEnableExperimentalExtensionApis); + } +}; + +} // namespace + +// Ensure that an isolated app never shares a process with WebUIs, non-isolated +// extensions, and normal webpages. None of these should ever comingle +// RenderProcessHosts even if we hit the process limit. +IN_PROC_BROWSER_TEST_F(ProcessManagementTest, ProcessOverflow) { + // Set max renderers to 1 to force running out of processes. + content::RenderProcessHost::SetMaxRendererProcessCount(1); + + host_resolver()->AddRule("*", "127.0.0.1"); + ASSERT_TRUE(test_server()->Start()); + + ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("isolated_apps/app1"))); + ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("isolated_apps/app2"))); + ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("hosted_app"))); + ASSERT_TRUE( + LoadExtension(test_data_dir_.AppendASCII("api_test/app_process"))); + + // The app under test acts on URLs whose host is "localhost", + // so the URLs we navigate to must have host "localhost". + GURL base_url = test_server()->GetURL( + "files/extensions/"); + GURL::Replacements replace_host; + std::string host_str("localhost"); // Must stay in scope with replace_host. + replace_host.SetHostStr(host_str); + base_url = base_url.ReplaceComponents(replace_host); + + // Load an extension before adding tabs. + const Extension* extension1 = LoadExtension( + test_data_dir_.AppendASCII("api_test/browser_action/basics")); + ASSERT_TRUE(extension1); + GURL extension1_url = extension1->url(); + + // Create multiple tabs for each type of renderer that might exist. + ui_test_utils::NavigateToURLWithDisposition( + browser(), base_url.Resolve("isolated_apps/app1/main.html"), + CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); + ui_test_utils::NavigateToURLWithDisposition( + browser(), GURL(chrome::kChromeUINewTabURL), + NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); + ui_test_utils::NavigateToURLWithDisposition( + browser(), base_url.Resolve("hosted_app/main.html"), + NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); + ui_test_utils::NavigateToURLWithDisposition( + browser(), base_url.Resolve("test_file.html"), + NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); + + ui_test_utils::NavigateToURLWithDisposition( + browser(), base_url.Resolve("isolated_apps/app2/main.html"), + NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); + ui_test_utils::NavigateToURLWithDisposition( + browser(), GURL(chrome::kChromeUINewTabURL), + NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); + ui_test_utils::NavigateToURLWithDisposition( + browser(), base_url.Resolve("api_test/app_process/path1/empty.html"), + NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); + ui_test_utils::NavigateToURLWithDisposition( + browser(), base_url.Resolve("test_file_with_body.html"), + NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); + + // Load another copy of isolated app 1. + ui_test_utils::NavigateToURLWithDisposition( + browser(), base_url.Resolve("isolated_apps/app1/main.html"), + NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); + + // Load another extension. + const Extension* extension2 = LoadExtension( + test_data_dir_.AppendASCII("api_test/browser_action/close_background")); + ASSERT_TRUE(extension2); + GURL extension2_url = extension2->url(); + + // Get tab processes. + ASSERT_EQ(9, browser()->tab_count()); + content::RenderProcessHost* isolated1_host = + browser()->GetWebContentsAt(0)->GetRenderProcessHost(); + content::RenderProcessHost* ntp1_host = + browser()->GetWebContentsAt(1)->GetRenderProcessHost(); + content::RenderProcessHost* hosted1_host = + browser()->GetWebContentsAt(2)->GetRenderProcessHost(); + content::RenderProcessHost* web1_host = + browser()->GetWebContentsAt(3)->GetRenderProcessHost(); + + content::RenderProcessHost* isolated2_host = + browser()->GetWebContentsAt(4)->GetRenderProcessHost(); + content::RenderProcessHost* ntp2_host = + browser()->GetWebContentsAt(5)->GetRenderProcessHost(); + content::RenderProcessHost* hosted2_host = + browser()->GetWebContentsAt(6)->GetRenderProcessHost(); + content::RenderProcessHost* web2_host = + browser()->GetWebContentsAt(7)->GetRenderProcessHost(); + + content::RenderProcessHost* second_isolated1_host = + browser()->GetWebContentsAt(8)->GetRenderProcessHost(); + + // Get extension processes. + ExtensionProcessManager* process_manager = + browser()->GetProfile()->GetExtensionProcessManager(); + content::RenderProcessHost* extension1_host = + process_manager->GetSiteInstanceForURL(extension1_url)->GetProcess(); + content::RenderProcessHost* extension2_host = + process_manager->GetSiteInstanceForURL(extension2_url)->GetProcess(); + + // An isolated app only shares with other instances of itself, not other + // isolated apps or anything else. + EXPECT_EQ(isolated1_host, second_isolated1_host); + EXPECT_NE(isolated1_host, isolated2_host); + EXPECT_NE(isolated1_host, ntp1_host); + EXPECT_NE(isolated1_host, hosted1_host); + EXPECT_NE(isolated1_host, web1_host); + EXPECT_NE(isolated1_host, extension1_host); + EXPECT_NE(isolated2_host, ntp1_host); + EXPECT_NE(isolated2_host, hosted1_host); + EXPECT_NE(isolated2_host, web1_host); + EXPECT_NE(isolated2_host, extension1_host); + + // Everything else is clannish. WebUI only shares with other WebUI. + EXPECT_EQ(ntp1_host, ntp2_host); + EXPECT_NE(ntp1_host, hosted1_host); + EXPECT_NE(ntp1_host, web1_host); + EXPECT_NE(ntp1_host, extension1_host); + + // Hosted apps only share with each other. + EXPECT_EQ(hosted1_host, hosted2_host); + EXPECT_NE(hosted1_host, web1_host); + EXPECT_NE(hosted1_host, extension1_host); + + // Web pages only share with each other. + EXPECT_EQ(web1_host, web2_host); + EXPECT_NE(web1_host, extension1_host); + + // Extensions only share with each other. + EXPECT_EQ(extension1_host, extension2_host); +} + +// Test to verify that the policy of maximum share of extension processes is +// properly enforced. +IN_PROC_BROWSER_TEST_F(ProcessManagementTest, ExtensionProcessBalancing) { + // Set max renderers to 6 so we can expect 2 extension processes to be + // allocated. + content::RenderProcessHost::SetMaxRendererProcessCount(6); + + // The app under test acts on URLs whose host is "localhost", + // so the URLs we navigate to must have host "localhost". + GURL base_url = test_server()->GetURL( + "files/extensions/"); + GURL::Replacements replace_host; + std::string host_str("localhost"); // Must stay in scope with replace_host. + replace_host.SetHostStr(host_str); + base_url = base_url.ReplaceComponents(replace_host); + + host_resolver()->AddRule("*", "127.0.0.1"); + ASSERT_TRUE(test_server()->Start()); + + ASSERT_TRUE(LoadExtension( + test_data_dir_.AppendASCII("api_test/browser_action/none"))); + ASSERT_TRUE(LoadExtension( + test_data_dir_.AppendASCII("api_test/browser_action/basics"))); + ASSERT_TRUE(LoadExtension( + test_data_dir_.AppendASCII("api_test/browser_action/remove_popup"))); + ASSERT_TRUE(LoadExtension( + test_data_dir_.AppendASCII("api_test/browser_action/add_popup"))); + ASSERT_TRUE(LoadExtension( + test_data_dir_.AppendASCII("api_test/browser_action/no_icon"))); + ASSERT_TRUE(LoadExtension( + test_data_dir_.AppendASCII("isolated_apps/app1"))); + ASSERT_TRUE(LoadExtension( + test_data_dir_.AppendASCII("api_test/management/test"))); + + ui_test_utils::NavigateToURLWithDisposition( + browser(), base_url.Resolve("isolated_apps/app1/main.html"), + CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); + + ui_test_utils::NavigateToURLWithDisposition( + browser(), base_url.Resolve("api_test/management/test/basics.html"), + CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); + + std::set<int> process_ids; + Profile* profile = browser()->GetProfile(); + ExtensionProcessManager* epm = profile->GetExtensionProcessManager(); + + for (ExtensionProcessManager::const_iterator iter = epm->begin(); + iter != epm->end(); + ++iter) { + ExtensionHost* host = *iter; + if (host->extension()->has_background_page()) { + process_ids.insert(host->render_process_host()->GetID()); + } + } + + // We've loaded 5 extensions with background pages, 1 extension without + // background page, and one isolated app. We expect only 2 unique processes + // hosting those extensions. + ASSERT_EQ((size_t) 5, profile->GetExtensionService()->process_map()->size()); + ASSERT_EQ((size_t) 2, process_ids.size()); +} diff --git a/chrome/browser/prerender/prerender_manager.cc b/chrome/browser/prerender/prerender_manager.cc index e4a82e9..681a76b 100644 --- a/chrome/browser/prerender/prerender_manager.cc +++ b/chrome/browser/prerender/prerender_manager.cc @@ -843,7 +843,8 @@ bool PrerenderManager::AddPrerender( // On Android we do reuse processes as we have a limited number of them and we // still want the benefits of prerendering even when several tabs are open. #if !defined(OS_ANDROID) - if (content::RenderProcessHost::ShouldTryToUseExistingProcessHost() && + if (content::RenderProcessHost::ShouldTryToUseExistingProcessHost( + profile_, url) && !content::RenderProcessHost::run_renderer_in_process()) { RecordFinalStatus(origin, experiment, FINAL_STATUS_TOO_MANY_PROCESSES); return false; diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 6f57abf..89cb276 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -2677,6 +2677,7 @@ 'browser/extensions/notifications_apitest.cc', 'browser/extensions/page_action_apitest.cc', 'browser/extensions/platform_app_browsertest.cc', + 'browser/extensions/process_management_browsertest.cc', 'browser/extensions/settings/settings_apitest.cc', 'browser/extensions/stubs_apitest.cc', 'browser/extensions/system/system_apitest.cc', diff --git a/chrome/common/chrome_constants.cc b/chrome/common/chrome_constants.cc index 62f68d7..2010d53 100644 --- a/chrome/common/chrome_constants.cc +++ b/chrome/common/chrome_constants.cc @@ -185,6 +185,8 @@ const int kJavascriptMessageExpectedDelay = 1000; const bool kEnableTouchIcon = false; +const float kMaxShareOfExtensionProcesses = 0.30f; + #if defined(OS_LINUX) extern const int kLowestRendererOomScore = 300; extern const int kHighestRendererOomScore = 1000; diff --git a/chrome/common/chrome_constants.h b/chrome/common/chrome_constants.h index 99f9f16..a5ac026 100644 --- a/chrome/common/chrome_constants.h +++ b/chrome/common/chrome_constants.h @@ -108,6 +108,13 @@ extern const int kJavascriptMessageExpectedDelay; // Are touch icons enabled? False by default. extern const bool kEnableTouchIcon; +// Fraction of the total number of processes to be used for hosting +// extensions. If we have more extensions than this percentage, we will start +// combining extensions in existing processes. This allows web pages to have +// enough render processes and not be starved when a lot of extensions are +// installed. +extern const float kMaxShareOfExtensionProcesses; + #if defined(OS_LINUX) // The highest and lowest assigned OOM score adjustment // (oom_score_adj) used by the OomPriority Manager. diff --git a/content/browser/mock_content_browser_client.cc b/content/browser/mock_content_browser_client.cc index 573b204..b7aab7b 100644 --- a/content/browser/mock_content_browser_client.cc +++ b/content/browser/mock_content_browser_client.cc @@ -63,6 +63,11 @@ bool MockContentBrowserClient::IsSuitableHost( return true; } +bool MockContentBrowserClient::ShouldTryToUseExistingProcessHost( + BrowserContext* browser_context, const GURL& url) { + return false; +} + void MockContentBrowserClient::SiteInstanceGotProcess( SiteInstance* site_instance) { } diff --git a/content/browser/mock_content_browser_client.h b/content/browser/mock_content_browser_client.h index 551f7f1..515d315 100644 --- a/content/browser/mock_content_browser_client.h +++ b/content/browser/mock_content_browser_client.h @@ -37,6 +37,8 @@ class MockContentBrowserClient : public ContentBrowserClient { virtual bool IsHandledURL(const GURL& url) OVERRIDE; virtual bool IsSuitableHost(RenderProcessHost* process_host, const GURL& site_url) OVERRIDE; + virtual bool ShouldTryToUseExistingProcessHost( + BrowserContext* browser_context, const GURL& url) OVERRIDE; virtual void SiteInstanceGotProcess(SiteInstance* site_instance) OVERRIDE; virtual void SiteInstanceDeleting(SiteInstance* site_instance) OVERRIDE; virtual bool ShouldSwapProcessesForNavigation(const GURL& current_url, diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index 6e17b9c..ccd1ece 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc @@ -1138,17 +1138,22 @@ content::RenderProcessHost* content::RenderProcessHost::FromID( } // static -bool content::RenderProcessHost::ShouldTryToUseExistingProcessHost() { - size_t renderer_process_count = g_all_hosts.Get().size(); +bool content::RenderProcessHost::ShouldTryToUseExistingProcessHost( + BrowserContext* browser_context, const GURL& url) { + + if (run_renderer_in_process()) + return true; // NOTE: Sometimes it's necessary to create more render processes than // GetMaxRendererProcessCount(), for instance when we want to create // a renderer process for a browser context that has no existing // renderers. This is OK in moderation, since the // GetMaxRendererProcessCount() is conservative. + if (g_all_hosts.Get().size() >= GetMaxRendererProcessCount()) + return true; - return run_renderer_in_process() || - (renderer_process_count >= GetMaxRendererProcessCount()); + return content::GetContentClient()->browser()-> + ShouldTryToUseExistingProcessHost(browser_context, url); } // static diff --git a/content/browser/site_instance_impl.cc b/content/browser/site_instance_impl.cc index ee9d042..792afc5 100644 --- a/content/browser/site_instance_impl.cc +++ b/content/browser/site_instance_impl.cc @@ -77,7 +77,8 @@ content::RenderProcessHost* SiteInstanceImpl::GetProcess() { // Create a new process if ours went away or was reused. if (!process_) { // See if we should reuse an old process - if (content::RenderProcessHost::ShouldTryToUseExistingProcessHost()) + if (content::RenderProcessHost::ShouldTryToUseExistingProcessHost( + browsing_instance_->browser_context(), site_)) process_ = content::RenderProcessHost::GetExistingProcessHost( browsing_instance_->browser_context(), site_); diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h index d8c842f..dab9721 100644 --- a/content/public/browser/content_browser_client.h +++ b/content/public/browser/content_browser_client.h @@ -120,6 +120,12 @@ class ContentBrowserClient { virtual bool IsSuitableHost(content::RenderProcessHost* process_host, const GURL& site_url) = 0; + // Returns whether a new process should be created or an existing one should + // be reused based on the URL we want to load. This should return false, + // unless there is a good reason otherwise. + virtual bool ShouldTryToUseExistingProcessHost( + BrowserContext* browser_context, const GURL& url) = 0; + // Called when a site instance is first associated with a process. virtual void SiteInstanceGotProcess( content::SiteInstance* site_instance) = 0; diff --git a/content/public/browser/render_process_host.h b/content/public/browser/render_process_host.h index adb08728..189ede6 100644 --- a/content/public/browser/render_process_host.h +++ b/content/public/browser/render_process_host.h @@ -208,7 +208,8 @@ class CONTENT_EXPORT RenderProcessHost : public IPC::Message::Sender, // Returns true if the caller should attempt to use an existing // RenderProcessHost rather than creating a new one. - static bool ShouldTryToUseExistingProcessHost(); + static bool ShouldTryToUseExistingProcessHost( + content::BrowserContext* browser_context, const GURL& site_url); // Get an existing RenderProcessHost associated with the given browser // context, if possible. The renderer process is chosen randomly from diff --git a/content/shell/shell_content_browser_client.cc b/content/shell/shell_content_browser_client.cc index 14aa1dd..d1b46a4 100644 --- a/content/shell/shell_content_browser_client.cc +++ b/content/shell/shell_content_browser_client.cc @@ -85,6 +85,11 @@ bool ShellContentBrowserClient::IsSuitableHost( return true; } +bool ShellContentBrowserClient::ShouldTryToUseExistingProcessHost( + BrowserContext* browser_context, const GURL& url) { + return false; +} + void ShellContentBrowserClient::SiteInstanceGotProcess( SiteInstance* site_instance) { } diff --git a/content/shell/shell_content_browser_client.h b/content/shell/shell_content_browser_client.h index 4cd84ab..d5c0913 100644 --- a/content/shell/shell_content_browser_client.h +++ b/content/shell/shell_content_browser_client.h @@ -41,6 +41,8 @@ class ShellContentBrowserClient : public ContentBrowserClient { virtual bool IsHandledURL(const GURL& url) OVERRIDE; virtual bool IsSuitableHost(RenderProcessHost* process_host, const GURL& site_url) OVERRIDE; + virtual bool ShouldTryToUseExistingProcessHost( + BrowserContext* browser_context, const GURL& url) OVERRIDE; virtual void SiteInstanceGotProcess(SiteInstance* site_instance) OVERRIDE; virtual void SiteInstanceDeleting(SiteInstance* site_instance) OVERRIDE; virtual bool ShouldSwapProcessesForNavigation(const GURL& current_url, |