Home | History | Annotate | Download | only in extensions
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/strings/utf_string_conversions.h"
      6 #include "chrome/browser/extensions/extension_apitest.h"
      7 #include "chrome/browser/extensions/extension_host.h"
      8 #include "chrome/browser/extensions/extension_process_manager.h"
      9 #include "chrome/browser/extensions/extension_service.h"
     10 #include "chrome/browser/extensions/extension_system.h"
     11 #include "chrome/browser/profiles/profile.h"
     12 #include "chrome/browser/profiles/profile_manager.h"
     13 #include "chrome/browser/ui/browser.h"
     14 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     15 #include "chrome/common/url_constants.h"
     16 #include "chrome/test/base/ui_test_utils.h"
     17 #include "content/public/browser/render_process_host.h"
     18 #include "content/public/browser/render_view_host.h"
     19 #include "content/public/browser/site_instance.h"
     20 #include "content/public/browser/web_contents.h"
     21 #include "extensions/common/switches.h"
     22 #include "net/dns/mock_host_resolver.h"
     23 #include "net/test/embedded_test_server/embedded_test_server.h"
     24 
     25 using content::NavigationController;
     26 using content::WebContents;
     27 
     28 namespace {
     29 
     30 class ProcessManagementTest : public ExtensionBrowserTest {
     31  private:
     32   // This is needed for testing isolated apps, which are still experimental.
     33   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
     34     ExtensionBrowserTest::SetUpCommandLine(command_line);
     35     command_line->AppendSwitch(
     36         extensions::switches::kEnableExperimentalExtensionApis);
     37   }
     38 };
     39 
     40 }  // namespace
     41 
     42 
     43 // TODO(nasko): crbug.com/173137
     44 #if defined(OS_WIN)
     45 #define MAYBE_ProcessOverflow DISABLED_ProcessOverflow
     46 #else
     47 #define MAYBE_ProcessOverflow ProcessOverflow
     48 #endif
     49 
     50 // Ensure that an isolated app never shares a process with WebUIs, non-isolated
     51 // extensions, and normal webpages.  None of these should ever comingle
     52 // RenderProcessHosts even if we hit the process limit.
     53 IN_PROC_BROWSER_TEST_F(ProcessManagementTest, MAYBE_ProcessOverflow) {
     54   // Set max renderers to 1 to force running out of processes.
     55   content::RenderProcessHost::SetMaxRendererProcessCount(1);
     56 
     57   host_resolver()->AddRule("*", "127.0.0.1");
     58   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
     59 
     60   ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("isolated_apps/app1")));
     61   ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("isolated_apps/app2")));
     62   ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("hosted_app")));
     63   ASSERT_TRUE(
     64       LoadExtension(test_data_dir_.AppendASCII("api_test/app_process")));
     65 
     66   // The app under test acts on URLs whose host is "localhost",
     67   // so the URLs we navigate to must have host "localhost".
     68   GURL base_url = embedded_test_server()->GetURL(
     69       "/extensions/");
     70   GURL::Replacements replace_host;
     71   std::string host_str("localhost");  // Must stay in scope with replace_host.
     72   replace_host.SetHostStr(host_str);
     73   base_url = base_url.ReplaceComponents(replace_host);
     74 
     75   // Load an extension before adding tabs.
     76   const extensions::Extension* extension1 = LoadExtension(
     77       test_data_dir_.AppendASCII("api_test/browser_action/basics"));
     78   ASSERT_TRUE(extension1);
     79   GURL extension1_url = extension1->url();
     80 
     81   // Create multiple tabs for each type of renderer that might exist.
     82   ui_test_utils::NavigateToURLWithDisposition(
     83       browser(), base_url.Resolve("isolated_apps/app1/main.html"),
     84       CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
     85   ui_test_utils::NavigateToURLWithDisposition(
     86       browser(), GURL(chrome::kChromeUINewTabURL),
     87       NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
     88   ui_test_utils::NavigateToURLWithDisposition(
     89       browser(), base_url.Resolve("hosted_app/main.html"),
     90       NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
     91   ui_test_utils::NavigateToURLWithDisposition(
     92       browser(), base_url.Resolve("test_file.html"),
     93       NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
     94 
     95   ui_test_utils::NavigateToURLWithDisposition(
     96       browser(), base_url.Resolve("isolated_apps/app2/main.html"),
     97       NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
     98   ui_test_utils::NavigateToURLWithDisposition(
     99       browser(), GURL(chrome::kChromeUINewTabURL),
    100       NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
    101   ui_test_utils::NavigateToURLWithDisposition(
    102       browser(), base_url.Resolve("api_test/app_process/path1/empty.html"),
    103       NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
    104   ui_test_utils::NavigateToURLWithDisposition(
    105       browser(), base_url.Resolve("test_file_with_body.html"),
    106       NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
    107 
    108   // Load another copy of isolated app 1.
    109   ui_test_utils::NavigateToURLWithDisposition(
    110       browser(), base_url.Resolve("isolated_apps/app1/main.html"),
    111       NEW_FOREGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
    112 
    113   // Load another extension.
    114   const extensions::Extension* extension2 = LoadExtension(
    115       test_data_dir_.AppendASCII("api_test/browser_action/close_background"));
    116   ASSERT_TRUE(extension2);
    117   GURL extension2_url = extension2->url();
    118 
    119   // Get tab processes.
    120   ASSERT_EQ(9, browser()->tab_strip_model()->count());
    121   content::RenderProcessHost* isolated1_host =
    122       browser()->tab_strip_model()->GetWebContentsAt(0)->GetRenderProcessHost();
    123   content::RenderProcessHost* ntp1_host =
    124       browser()->tab_strip_model()->GetWebContentsAt(1)->GetRenderProcessHost();
    125   content::RenderProcessHost* hosted1_host =
    126       browser()->tab_strip_model()->GetWebContentsAt(2)->GetRenderProcessHost();
    127   content::RenderProcessHost* web1_host =
    128       browser()->tab_strip_model()->GetWebContentsAt(3)->GetRenderProcessHost();
    129 
    130   content::RenderProcessHost* isolated2_host =
    131       browser()->tab_strip_model()->GetWebContentsAt(4)->GetRenderProcessHost();
    132   content::RenderProcessHost* ntp2_host =
    133       browser()->tab_strip_model()->GetWebContentsAt(5)->GetRenderProcessHost();
    134   content::RenderProcessHost* hosted2_host =
    135       browser()->tab_strip_model()->GetWebContentsAt(6)->GetRenderProcessHost();
    136   content::RenderProcessHost* web2_host =
    137       browser()->tab_strip_model()->GetWebContentsAt(7)->GetRenderProcessHost();
    138 
    139   content::RenderProcessHost* second_isolated1_host =
    140       browser()->tab_strip_model()->GetWebContentsAt(8)->GetRenderProcessHost();
    141 
    142   // Get extension processes.
    143   ExtensionProcessManager* process_manager =
    144       extensions::ExtensionSystem::Get(browser()->profile())->
    145           process_manager();
    146   content::RenderProcessHost* extension1_host =
    147       process_manager->GetSiteInstanceForURL(extension1_url)->GetProcess();
    148   content::RenderProcessHost* extension2_host =
    149       process_manager->GetSiteInstanceForURL(extension2_url)->GetProcess();
    150 
    151   // An isolated app only shares with other instances of itself, not other
    152   // isolated apps or anything else.
    153   EXPECT_EQ(isolated1_host, second_isolated1_host);
    154   EXPECT_NE(isolated1_host, isolated2_host);
    155   EXPECT_NE(isolated1_host, ntp1_host);
    156   EXPECT_NE(isolated1_host, hosted1_host);
    157   EXPECT_NE(isolated1_host, web1_host);
    158   EXPECT_NE(isolated1_host, extension1_host);
    159   EXPECT_NE(isolated2_host, ntp1_host);
    160   EXPECT_NE(isolated2_host, hosted1_host);
    161   EXPECT_NE(isolated2_host, web1_host);
    162   EXPECT_NE(isolated2_host, extension1_host);
    163 
    164   // Everything else is clannish.  WebUI only shares with other WebUI.
    165   EXPECT_EQ(ntp1_host, ntp2_host);
    166   EXPECT_NE(ntp1_host, hosted1_host);
    167   EXPECT_NE(ntp1_host, web1_host);
    168   EXPECT_NE(ntp1_host, extension1_host);
    169 
    170   // Hosted apps only share with each other.
    171   // Note that hosted2_host's app has the background permission and will use
    172   // process-per-site mode, but it should still share with hosted1_host's app.
    173   EXPECT_EQ(hosted1_host, hosted2_host);
    174   EXPECT_NE(hosted1_host, web1_host);
    175   EXPECT_NE(hosted1_host, extension1_host);
    176 
    177   // Web pages only share with each other.
    178   EXPECT_EQ(web1_host, web2_host);
    179   EXPECT_NE(web1_host, extension1_host);
    180 
    181   // Extensions only share with each other.
    182   EXPECT_EQ(extension1_host, extension2_host);
    183 }
    184 
    185 // See
    186 #if defined(OS_WIN)
    187 #define MAYBE_ExtensionProcessBalancing DISABLED_ExtensionProcessBalancing
    188 #else
    189 #define MAYBE_ExtensionProcessBalancing ExtensionProcessBalancing
    190 #endif
    191 // Test to verify that the policy of maximum share of extension processes is
    192 // properly enforced.
    193 IN_PROC_BROWSER_TEST_F(ProcessManagementTest, MAYBE_ExtensionProcessBalancing) {
    194   // Set max renderers to 6 so we can expect 2 extension processes to be
    195   // allocated.
    196   content::RenderProcessHost::SetMaxRendererProcessCount(6);
    197 
    198   host_resolver()->AddRule("*", "127.0.0.1");
    199   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
    200 
    201   // The app under test acts on URLs whose host is "localhost",
    202   // so the URLs we navigate to must have host "localhost".
    203   GURL base_url = embedded_test_server()->GetURL(
    204       "/extensions/");
    205   GURL::Replacements replace_host;
    206   std::string host_str("localhost");  // Must stay in scope with replace_host.
    207   replace_host.SetHostStr(host_str);
    208   base_url = base_url.ReplaceComponents(replace_host);
    209 
    210   ASSERT_TRUE(LoadExtension(
    211       test_data_dir_.AppendASCII("api_test/browser_action/none")));
    212   ASSERT_TRUE(LoadExtension(
    213       test_data_dir_.AppendASCII("api_test/browser_action/basics")));
    214   ASSERT_TRUE(LoadExtension(
    215       test_data_dir_.AppendASCII("api_test/browser_action/remove_popup")));
    216   ASSERT_TRUE(LoadExtension(
    217       test_data_dir_.AppendASCII("api_test/browser_action/add_popup")));
    218   ASSERT_TRUE(LoadExtension(
    219       test_data_dir_.AppendASCII("api_test/browser_action/no_icon")));
    220   ASSERT_TRUE(LoadExtension(
    221       test_data_dir_.AppendASCII("isolated_apps/app1")));
    222   ASSERT_TRUE(LoadExtension(
    223       test_data_dir_.AppendASCII("api_test/management/test")));
    224 
    225   ui_test_utils::NavigateToURLWithDisposition(
    226       browser(), base_url.Resolve("isolated_apps/app1/main.html"),
    227       CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
    228 
    229   ui_test_utils::NavigateToURLWithDisposition(
    230       browser(), base_url.Resolve("api_test/management/test/basics.html"),
    231       CURRENT_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
    232 
    233   std::set<int> process_ids;
    234   Profile* profile = browser()->profile();
    235   ExtensionProcessManager* epm = extensions::ExtensionSystem::Get(profile)->
    236       process_manager();
    237   for (ExtensionProcessManager::const_iterator iter =
    238            epm->background_hosts().begin();
    239        iter != epm->background_hosts().end(); ++iter) {
    240     process_ids.insert((*iter)->render_process_host()->GetID());
    241   }
    242 
    243   // We've loaded 5 extensions with background pages, 1 extension without
    244   // background page, and one isolated app. We expect only 2 unique processes
    245   // hosting those extensions.
    246   ExtensionService* service =
    247       extensions::ExtensionSystem::Get(profile)->extension_service();
    248 
    249   EXPECT_GE((size_t) 6, service->process_map()->size());
    250   EXPECT_EQ((size_t) 2, process_ids.size());
    251 }
    252