summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authornasko@chromium.org <nasko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-02-22 18:56:06 +0000
committernasko@chromium.org <nasko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-02-22 18:56:06 +0000
commit76411f41ebe3a918f3108af85646d6e1fca604e8 (patch)
treed5510d46abd196864ab533253f5f7817a81a0fa4
parentb37db0786972fdd10c2977576a5db3c625215ceb (diff)
downloadchromium_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.cc55
-rw-r--r--chrome/browser/chrome_content_browser_client.h2
-rw-r--r--chrome/browser/extensions/isolated_app_browsertest.cc132
-rw-r--r--chrome/browser/extensions/process_management_browsertest.cc232
-rw-r--r--chrome/browser/prerender/prerender_manager.cc3
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--chrome/common/chrome_constants.cc2
-rw-r--r--chrome/common/chrome_constants.h7
-rw-r--r--content/browser/mock_content_browser_client.cc5
-rw-r--r--content/browser/mock_content_browser_client.h2
-rw-r--r--content/browser/renderer_host/render_process_host_impl.cc13
-rw-r--r--content/browser/site_instance_impl.cc3
-rw-r--r--content/public/browser/content_browser_client.h6
-rw-r--r--content/public/browser/render_process_host.h3
-rw-r--r--content/shell/shell_content_browser_client.cc5
-rw-r--r--content/shell/shell_content_browser_client.h2
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,