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/command_line.h"
      6 #include "chrome/browser/chrome_notification_types.h"
      7 #include "chrome/browser/extensions/extension_apitest.h"
      8 #include "chrome/browser/extensions/extension_host.h"
      9 #include "chrome/browser/extensions/extension_service.h"
     10 #include "chrome/browser/extensions/extension_system.h"
     11 #include "chrome/browser/extensions/process_map.h"
     12 #include "chrome/browser/profiles/profile.h"
     13 #include "chrome/browser/ui/blocked_content/blocked_content_tab_helper.h"
     14 #include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h"
     15 #include "chrome/browser/ui/browser.h"
     16 #include "chrome/browser/ui/browser_commands.h"
     17 #include "chrome/browser/ui/browser_finder.h"
     18 #include "chrome/browser/ui/browser_list.h"
     19 #include "chrome/browser/ui/browser_window.h"
     20 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     21 #include "chrome/common/chrome_switches.h"
     22 #include "chrome/common/extensions/extension.h"
     23 #include "chrome/common/extensions/extension_file_util.h"
     24 #include "chrome/test/base/test_switches.h"
     25 #include "chrome/test/base/ui_test_utils.h"
     26 #include "content/public/browser/navigation_entry.h"
     27 #include "content/public/browser/notification_service.h"
     28 #include "content/public/browser/render_process_host.h"
     29 #include "content/public/browser/render_view_host.h"
     30 #include "content/public/browser/web_contents.h"
     31 #include "content/public/test/browser_test_utils.h"
     32 #include "content/public/test/test_navigation_observer.h"
     33 #include "net/dns/mock_host_resolver.h"
     34 #include "net/test/embedded_test_server/embedded_test_server.h"
     35 #include "sync/api/string_ordinal.h"
     36 
     37 using content::NavigationController;
     38 using content::RenderViewHost;
     39 using content::WebContents;
     40 using extensions::Extension;
     41 
     42 class AppApiTest : public ExtensionApiTest {
     43  protected:
     44   // Gets the base URL for files for a specific test, making sure that it uses
     45   // "localhost" as the hostname, since that is what the extent is declared
     46   // as in the test apps manifests.
     47   GURL GetTestBaseURL(std::string test_directory) {
     48     GURL::Replacements replace_host;
     49     std::string host_str("localhost");  // must stay in scope with replace_host
     50     replace_host.SetHostStr(host_str);
     51     GURL base_url = embedded_test_server()->GetURL(
     52         "/extensions/api_test/" + test_directory + "/");
     53     return base_url.ReplaceComponents(replace_host);
     54   }
     55 
     56   // Pass flags to make testing apps easier.
     57   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
     58     ExtensionApiTest::SetUpCommandLine(command_line);
     59     CommandLine::ForCurrentProcess()->AppendSwitch(
     60         switches::kDisablePopupBlocking);
     61     CommandLine::ForCurrentProcess()->AppendSwitch(
     62         switches::kAllowHTTPBackgroundPage);
     63   }
     64 
     65   // Helper function to test that independent tabs of the named app are loaded
     66   // into separate processes.
     67   void TestAppInstancesHelper(std::string app_name) {
     68     LOG(INFO) << "Start of test.";
     69 
     70     extensions::ProcessMap* process_map = extensions::ExtensionSystem::Get(
     71         browser()->profile())->extension_service()->process_map();
     72 
     73     host_resolver()->AddRule("*", "127.0.0.1");
     74     ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
     75 
     76     ASSERT_TRUE(LoadExtension(
     77         test_data_dir_.AppendASCII(app_name)));
     78     const Extension* extension = GetSingleLoadedExtension();
     79 
     80     // Open two tabs in the app, one outside it.
     81     GURL base_url = GetTestBaseURL(app_name);
     82 
     83     // Test both opening a URL in a new tab, and opening a tab and then
     84     // navigating it.  Either way, app tabs should be considered extension
     85     // processes, but they have no elevated privileges and thus should not
     86     // have WebUI bindings.
     87     ui_test_utils::NavigateToURLWithDisposition(
     88         browser(), base_url.Resolve("path1/empty.html"), NEW_FOREGROUND_TAB,
     89         ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
     90     LOG(INFO) << "Nav 1.";
     91     EXPECT_TRUE(process_map->Contains(
     92         browser()->tab_strip_model()->GetWebContentsAt(1)->
     93             GetRenderProcessHost()->GetID()));
     94     EXPECT_FALSE(browser()->tab_strip_model()->GetWebContentsAt(1)->GetWebUI());
     95 
     96     content::WindowedNotificationObserver tab_added_observer(
     97         chrome::NOTIFICATION_TAB_ADDED,
     98         content::NotificationService::AllSources());
     99     chrome::NewTab(browser());
    100     tab_added_observer.Wait();
    101     LOG(INFO) << "New tab.";
    102     ui_test_utils::NavigateToURL(browser(),
    103                                  base_url.Resolve("path2/empty.html"));
    104     LOG(INFO) << "Nav 2.";
    105     EXPECT_TRUE(process_map->Contains(
    106         browser()->tab_strip_model()->GetWebContentsAt(2)->
    107             GetRenderProcessHost()->GetID()));
    108     EXPECT_FALSE(browser()->tab_strip_model()->GetWebContentsAt(2)->GetWebUI());
    109 
    110     // We should have opened 2 new extension tabs. Including the original blank
    111     // tab, we now have 3 tabs. The two app tabs should not be in the same
    112     // process, since they do not have the background permission.  (Thus, we
    113     // want to separate them to improve responsiveness.)
    114     ASSERT_EQ(3, browser()->tab_strip_model()->count());
    115     WebContents* tab1 = browser()->tab_strip_model()->GetWebContentsAt(1);
    116     WebContents* tab2 = browser()->tab_strip_model()->GetWebContentsAt(2);
    117     EXPECT_NE(tab1->GetRenderProcessHost(), tab2->GetRenderProcessHost());
    118 
    119     // Opening tabs with window.open should keep the page in the opener's
    120     // process.
    121     ASSERT_EQ(1u, chrome::GetBrowserCount(browser()->profile(),
    122                                           browser()->host_desktop_type()));
    123     OpenWindow(tab1, base_url.Resolve("path1/empty.html"), true, NULL);
    124     LOG(INFO) << "WindowOpenHelper 1.";
    125     OpenWindow(tab2, base_url.Resolve("path2/empty.html"), true, NULL);
    126     LOG(INFO) << "End of test.";
    127     UnloadExtension(extension->id());
    128   }
    129 };
    130 
    131 // Omits the disable-popup-blocking flag so we can cover that case.
    132 class BlockedAppApiTest : public AppApiTest {
    133  protected:
    134   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
    135     ExtensionApiTest::SetUpCommandLine(command_line);
    136     CommandLine::ForCurrentProcess()->AppendSwitch(
    137         switches::kAllowHTTPBackgroundPage);
    138   }
    139 };
    140 
    141 // Tests that hosted apps with the background permission get a process-per-app
    142 // model, since all pages need to be able to script the background page.
    143 // http://crbug.com/172750
    144 IN_PROC_BROWSER_TEST_F(AppApiTest, DISABLED_AppProcess) {
    145   LOG(INFO) << "Start of test.";
    146 
    147   extensions::ProcessMap* process_map = extensions::ExtensionSystem::Get(
    148       browser()->profile())->extension_service()->process_map();
    149 
    150   host_resolver()->AddRule("*", "127.0.0.1");
    151   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
    152 
    153   ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app_process")));
    154 
    155   LOG(INFO) << "Loaded extension.";
    156 
    157   // Open two tabs in the app, one outside it.
    158   GURL base_url = GetTestBaseURL("app_process");
    159 
    160   // Test both opening a URL in a new tab, and opening a tab and then navigating
    161   // it.  Either way, app tabs should be considered extension processes, but
    162   // they have no elevated privileges and thus should not have WebUI bindings.
    163   ui_test_utils::NavigateToURLWithDisposition(
    164       browser(), base_url.Resolve("path1/empty.html"), NEW_FOREGROUND_TAB,
    165       ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
    166   EXPECT_TRUE(process_map->Contains(
    167       browser()->tab_strip_model()->GetWebContentsAt(1)->
    168           GetRenderProcessHost()->GetID()));
    169   EXPECT_FALSE(browser()->tab_strip_model()->GetWebContentsAt(1)->GetWebUI());
    170   LOG(INFO) << "Nav 1.";
    171 
    172   ui_test_utils::NavigateToURLWithDisposition(
    173       browser(), base_url.Resolve("path2/empty.html"), NEW_FOREGROUND_TAB,
    174       ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
    175   EXPECT_TRUE(process_map->Contains(
    176       browser()->tab_strip_model()->GetWebContentsAt(2)->
    177           GetRenderProcessHost()->GetID()));
    178   EXPECT_FALSE(browser()->tab_strip_model()->GetWebContentsAt(2)->GetWebUI());
    179   LOG(INFO) << "Nav 2.";
    180 
    181   content::WindowedNotificationObserver tab_added_observer(
    182       chrome::NOTIFICATION_TAB_ADDED,
    183       content::NotificationService::AllSources());
    184   chrome::NewTab(browser());
    185   tab_added_observer.Wait();
    186   LOG(INFO) << "New tab.";
    187   ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path3/empty.html"));
    188   LOG(INFO) << "Nav 3.";
    189   EXPECT_FALSE(process_map->Contains(
    190       browser()->tab_strip_model()->GetWebContentsAt(3)->
    191           GetRenderProcessHost()->GetID()));
    192   EXPECT_FALSE(browser()->tab_strip_model()->GetWebContentsAt(3)->GetWebUI());
    193 
    194   // We should have opened 3 new extension tabs. Including the original blank
    195   // tab, we now have 4 tabs. Because the app_process app has the background
    196   // permission, all of its instances are in the same process.  Thus two tabs
    197   // should be part of the extension app and grouped in the same process.
    198   ASSERT_EQ(4, browser()->tab_strip_model()->count());
    199   WebContents* tab = browser()->tab_strip_model()->GetWebContentsAt(1);
    200 
    201   EXPECT_EQ(tab->GetRenderProcessHost(),
    202             browser()->tab_strip_model()->GetWebContentsAt(2)->
    203                 GetRenderProcessHost());
    204   EXPECT_NE(tab->GetRenderProcessHost(),
    205             browser()->tab_strip_model()->GetWebContentsAt(3)->
    206                 GetRenderProcessHost());
    207 
    208   // Now let's do the same using window.open. The same should happen.
    209   ASSERT_EQ(1u, chrome::GetBrowserCount(browser()->profile(),
    210                                         browser()->host_desktop_type()));
    211   OpenWindow(tab, base_url.Resolve("path1/empty.html"), true, NULL);
    212   LOG(INFO) << "WindowOpenHelper 1.";
    213   OpenWindow(tab, base_url.Resolve("path2/empty.html"), true, NULL);
    214   LOG(INFO) << "WindowOpenHelper 2.";
    215   // TODO(creis): This should open in a new process (i.e., false for the last
    216   // argument), but we temporarily avoid swapping processes away from a hosted
    217   // app if it has an opener, because some OAuth providers make script calls
    218   // between non-app popups and non-app iframes in the app process.
    219   // See crbug.com/59285.
    220   OpenWindow(tab, base_url.Resolve("path3/empty.html"), true, NULL);
    221   LOG(INFO) << "WindowOpenHelper 3.";
    222 
    223   // Now let's have these pages navigate, into or out of the extension web
    224   // extent. They should switch processes.
    225   const GURL& app_url(base_url.Resolve("path1/empty.html"));
    226   const GURL& non_app_url(base_url.Resolve("path3/empty.html"));
    227   NavigateInRenderer(browser()->tab_strip_model()->GetWebContentsAt(2),
    228                      non_app_url);
    229   LOG(INFO) << "NavigateTabHelper 1.";
    230   NavigateInRenderer(browser()->tab_strip_model()->GetWebContentsAt(3),
    231                      app_url);
    232   LOG(INFO) << "NavigateTabHelper 2.";
    233   EXPECT_NE(tab->GetRenderProcessHost(),
    234             browser()->tab_strip_model()->GetWebContentsAt(2)->
    235                 GetRenderProcessHost());
    236   EXPECT_EQ(tab->GetRenderProcessHost(),
    237             browser()->tab_strip_model()->GetWebContentsAt(3)->
    238                 GetRenderProcessHost());
    239 
    240   // If one of the popup tabs navigates back to the app, window.opener should
    241   // be valid.
    242   NavigateInRenderer(browser()->tab_strip_model()->GetWebContentsAt(6),
    243                      app_url);
    244   LOG(INFO) << "NavigateTabHelper 3.";
    245   EXPECT_EQ(tab->GetRenderProcessHost(),
    246             browser()->tab_strip_model()->GetWebContentsAt(6)->
    247                 GetRenderProcessHost());
    248   bool windowOpenerValid = false;
    249   ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
    250       browser()->tab_strip_model()->GetWebContentsAt(6),
    251       "window.domAutomationController.send(window.opener != null)",
    252       &windowOpenerValid));
    253   ASSERT_TRUE(windowOpenerValid);
    254 
    255   LOG(INFO) << "End of test.";
    256 }
    257 
    258 // Test that hosted apps without the background permission use a process per app
    259 // instance model, such that separate instances are in separate processes.
    260 // Flaky on Windows. http://crbug.com/248047
    261 #if defined(OS_WIN)
    262 #define MAYBE_AppProcessInstances DISABLED_AppProcessInstances
    263 #else
    264 #define MAYBE_AppProcessInstances AppProcessInstances
    265 #endif
    266 IN_PROC_BROWSER_TEST_F(AppApiTest, MAYBE_AppProcessInstances) {
    267   TestAppInstancesHelper("app_process_instances");
    268 }
    269 
    270 // Test that hosted apps with the background permission but that set
    271 // allow_js_access to false also use a process per app instance model.
    272 // Separate instances should be in separate processes.
    273 // Flaky on XP: http://crbug.com/165834
    274 #if defined(OS_WIN)
    275 #define MAYBE_AppProcessBackgroundInstances \
    276     DISABLED_AppProcessBackgroundInstances
    277 #else
    278 #define MAYBE_AppProcessBackgroundInstances AppProcessBackgroundInstances
    279 #endif
    280 IN_PROC_BROWSER_TEST_F(AppApiTest, MAYBE_AppProcessBackgroundInstances) {
    281   TestAppInstancesHelper("app_process_background_instances");
    282 }
    283 
    284 // Tests that bookmark apps do not use the app process model and are treated
    285 // like normal web pages instead.  http://crbug.com/104636.
    286 // Timing out on Windows. http://crbug.com/238777
    287 #if defined(OS_WIN)
    288 #define MAYBE_BookmarkAppGetsNormalProcess DISABLED_BookmarkAppGetsNormalProcess
    289 #else
    290 #define MAYBE_BookmarkAppGetsNormalProcess BookmarkAppGetsNormalProcess
    291 #endif
    292 IN_PROC_BROWSER_TEST_F(AppApiTest, MAYBE_BookmarkAppGetsNormalProcess) {
    293   ExtensionService* service = extensions::ExtensionSystem::Get(
    294       browser()->profile())->extension_service();
    295   extensions::ProcessMap* process_map = service->process_map();
    296 
    297   host_resolver()->AddRule("*", "127.0.0.1");
    298   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
    299   GURL base_url = GetTestBaseURL("app_process");
    300 
    301   // Load an app as a bookmark app.
    302   std::string error;
    303   scoped_refptr<const Extension> extension(extension_file_util::LoadExtension(
    304       test_data_dir_.AppendASCII("app_process"),
    305       extensions::Manifest::UNPACKED,
    306       Extension::FROM_BOOKMARK,
    307       &error));
    308   service->OnExtensionInstalled(extension.get(),
    309                                 syncer::StringOrdinal::CreateInitialOrdinal(),
    310                                 false /* no requirement errors */,
    311                                 extensions::Blacklist::NOT_BLACKLISTED,
    312                                 false /* don't wait for idle */);
    313   ASSERT_TRUE(extension.get());
    314   ASSERT_TRUE(extension->from_bookmark());
    315 
    316   // Test both opening a URL in a new tab, and opening a tab and then navigating
    317   // it.  Either way, bookmark app tabs should be considered normal processes
    318   // with no elevated privileges and no WebUI bindings.
    319   ui_test_utils::NavigateToURLWithDisposition(
    320       browser(), base_url.Resolve("path1/empty.html"), NEW_FOREGROUND_TAB,
    321       ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
    322   EXPECT_FALSE(process_map->Contains(
    323       browser()->tab_strip_model()->GetWebContentsAt(1)->
    324           GetRenderProcessHost()->GetID()));
    325   EXPECT_FALSE(browser()->tab_strip_model()->GetWebContentsAt(1)->GetWebUI());
    326 
    327   content::WindowedNotificationObserver tab_added_observer(
    328       chrome::NOTIFICATION_TAB_ADDED,
    329       content::NotificationService::AllSources());
    330   chrome::NewTab(browser());
    331   tab_added_observer.Wait();
    332   ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path2/empty.html"));
    333   EXPECT_FALSE(process_map->Contains(
    334       browser()->tab_strip_model()->GetWebContentsAt(2)->
    335           GetRenderProcessHost()->GetID()));
    336   EXPECT_FALSE(browser()->tab_strip_model()->GetWebContentsAt(2)->GetWebUI());
    337 
    338   // We should have opened 2 new bookmark app tabs. Including the original blank
    339   // tab, we now have 3 tabs.  Because normal pages use the
    340   // process-per-site-instance model, each should be in its own process.
    341   ASSERT_EQ(3, browser()->tab_strip_model()->count());
    342   WebContents* tab = browser()->tab_strip_model()->GetWebContentsAt(1);
    343   EXPECT_NE(tab->GetRenderProcessHost(),
    344             browser()->tab_strip_model()->GetWebContentsAt(2)->
    345                 GetRenderProcessHost());
    346 
    347   // Now let's do the same using window.open. The same should happen.
    348   ASSERT_EQ(1u, chrome::GetBrowserCount(browser()->profile(),
    349                                         browser()->host_desktop_type()));
    350   OpenWindow(tab, base_url.Resolve("path1/empty.html"), true, NULL);
    351   OpenWindow(tab, base_url.Resolve("path2/empty.html"), true, NULL);
    352 
    353   // Now let's have a tab navigate out of and back into the app's web
    354   // extent. Neither navigation should switch processes.
    355   const GURL& app_url(base_url.Resolve("path1/empty.html"));
    356   const GURL& non_app_url(base_url.Resolve("path3/empty.html"));
    357   RenderViewHost* host2 =
    358       browser()->tab_strip_model()->GetWebContentsAt(2)->GetRenderViewHost();
    359   NavigateInRenderer(browser()->tab_strip_model()->GetWebContentsAt(2),
    360                      non_app_url);
    361   EXPECT_EQ(host2->GetProcess(),
    362             browser()->tab_strip_model()->GetWebContentsAt(2)->
    363                 GetRenderProcessHost());
    364   NavigateInRenderer(browser()->tab_strip_model()->GetWebContentsAt(2),
    365                      app_url);
    366   EXPECT_EQ(host2->GetProcess(),
    367             browser()->tab_strip_model()->GetWebContentsAt(2)->
    368                 GetRenderProcessHost());
    369 }
    370 
    371 // Tests that app process switching works properly in the following scenario:
    372 // 1. navigate to a page1 in the app
    373 // 2. page1 redirects to a page2 outside the app extent (ie, "/server-redirect")
    374 // 3. page2 redirects back to a page in the app
    375 // The final navigation should end up in the app process.
    376 // See http://crbug.com/61757
    377 IN_PROC_BROWSER_TEST_F(AppApiTest, AppProcessRedirectBack) {
    378   host_resolver()->AddRule("*", "127.0.0.1");
    379   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
    380 
    381   ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app_process")));
    382 
    383   // Open two tabs in the app.
    384   GURL base_url = GetTestBaseURL("app_process");
    385 
    386   chrome::NewTab(browser());
    387   ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
    388   chrome::NewTab(browser());
    389   // Wait until the second tab finishes its redirect train (2 hops).
    390   // 1. We navigate to redirect.html
    391   // 2. Renderer navigates and finishes, counting as a load stop.
    392   // 3. Renderer issues the meta refresh to navigate to server-redirect.
    393   // 4. Renderer is now in a "provisional load", waiting for navigation to
    394   //    complete.
    395   // 5. Browser sees a redirect response from server-redirect to empty.html, and
    396   //    transfers that to a new navigation, using RequestTransferURL.
    397   // 6. Renderer navigates to empty.html, and finishes loading, counting as the
    398   //    second load stop
    399   ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
    400       browser(), base_url.Resolve("path1/redirect.html"), 2);
    401 
    402   // 3 tabs, including the initial about:blank. The last 2 should be the same
    403   // process.
    404   ASSERT_EQ(3, browser()->tab_strip_model()->count());
    405   EXPECT_EQ("/extensions/api_test/app_process/path1/empty.html",
    406             browser()->tab_strip_model()->GetWebContentsAt(2)->
    407                 GetController().GetLastCommittedEntry()->GetURL().path());
    408   EXPECT_EQ(browser()->tab_strip_model()->GetWebContentsAt(1)->
    409                 GetRenderProcessHost(),
    410             browser()->tab_strip_model()->GetWebContentsAt(2)->
    411                 GetRenderProcessHost());
    412 }
    413 
    414 // Ensure that re-navigating to a URL after installing or uninstalling it as an
    415 // app correctly swaps the tab to the app process.  (http://crbug.com/80621)
    416 //
    417 // Fails on Windows. http://crbug.com/238670
    418 // Added logging to help diagnose the location of the problem.
    419 IN_PROC_BROWSER_TEST_F(AppApiTest, NavigateIntoAppProcess) {
    420   extensions::ProcessMap* process_map = extensions::ExtensionSystem::Get(
    421       browser()->profile())->extension_service()->process_map();
    422 
    423   host_resolver()->AddRule("*", "127.0.0.1");
    424   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
    425 
    426   // The app under test acts on URLs whose host is "localhost",
    427   // so the URLs we navigate to must have host "localhost".
    428   GURL base_url = GetTestBaseURL("app_process");
    429 
    430   // Load an app URL before loading the app.
    431   LOG(INFO) << "Loading path1/empty.html.";
    432   ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
    433   LOG(INFO) << "Loading path1/empty.html - done.";
    434   WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0);
    435   EXPECT_FALSE(process_map->Contains(
    436       contents->GetRenderProcessHost()->GetID()));
    437 
    438   // Load app and re-navigate to the page.
    439   LOG(INFO) << "Loading extension.";
    440   const Extension* app =
    441       LoadExtension(test_data_dir_.AppendASCII("app_process"));
    442   LOG(INFO) << "Loading extension - done.";
    443   ASSERT_TRUE(app);
    444   LOG(INFO) << "Loading path1/empty.html.";
    445   ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
    446   LOG(INFO) << "Loading path1/empty.html - done.";
    447   EXPECT_TRUE(process_map->Contains(
    448       contents->GetRenderProcessHost()->GetID()));
    449 
    450   // Disable app and re-navigate to the page.
    451   LOG(INFO) << "Disabling extension.";
    452   DisableExtension(app->id());
    453   LOG(INFO) << "Disabling extension - done.";
    454   LOG(INFO) << "Loading path1/empty.html.";
    455   ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
    456   LOG(INFO) << "Loading path1/empty.html - done.";
    457   EXPECT_FALSE(process_map->Contains(
    458       contents->GetRenderProcessHost()->GetID()));
    459 }
    460 
    461 // Ensure that reloading a URL after installing or uninstalling it as an app
    462 // correctly swaps the tab to the app process.  (http://crbug.com/80621)
    463 //
    464 // Added logging to help diagnose the location of the problem.
    465 // http://crbug.com/238670
    466 IN_PROC_BROWSER_TEST_F(AppApiTest, ReloadIntoAppProcess) {
    467   extensions::ProcessMap* process_map = extensions::ExtensionSystem::Get(
    468       browser()->profile())->extension_service()->process_map();
    469 
    470   host_resolver()->AddRule("*", "127.0.0.1");
    471   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
    472 
    473   // The app under test acts on URLs whose host is "localhost",
    474   // so the URLs we navigate to must have host "localhost".
    475   GURL base_url = GetTestBaseURL("app_process");
    476 
    477   // Load app, disable it, and navigate to the page.
    478   LOG(INFO) << "Loading extension.";
    479   const Extension* app =
    480       LoadExtension(test_data_dir_.AppendASCII("app_process"));
    481   LOG(INFO) << "Loading extension - done.";
    482   ASSERT_TRUE(app);
    483   LOG(INFO) << "Disabling extension.";
    484   DisableExtension(app->id());
    485   LOG(INFO) << "Disabling extension - done.";
    486   LOG(INFO) << "Navigate to path1/empty.html.";
    487   ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
    488   LOG(INFO) << "Navigate to path1/empty.html - done.";
    489   WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0);
    490   EXPECT_FALSE(process_map->Contains(
    491       contents->GetRenderProcessHost()->GetID()));
    492 
    493   // Enable app and reload the page.
    494   LOG(INFO) << "Enabling extension.";
    495   EnableExtension(app->id());
    496   LOG(INFO) << "Enabling extension - done.";
    497   content::WindowedNotificationObserver reload_observer(
    498       content::NOTIFICATION_LOAD_STOP,
    499       content::Source<NavigationController>(
    500           &browser()->tab_strip_model()->GetActiveWebContents()->
    501               GetController()));
    502   LOG(INFO) << "Reloading.";
    503   chrome::Reload(browser(), CURRENT_TAB);
    504   reload_observer.Wait();
    505   LOG(INFO) << "Reloading - done.";
    506   EXPECT_TRUE(process_map->Contains(
    507       contents->GetRenderProcessHost()->GetID()));
    508 
    509   // Disable app and reload the page.
    510   LOG(INFO) << "Disabling extension.";
    511   DisableExtension(app->id());
    512   LOG(INFO) << "Disabling extension - done.";
    513   content::WindowedNotificationObserver reload_observer2(
    514       content::NOTIFICATION_LOAD_STOP,
    515       content::Source<NavigationController>(
    516           &browser()->tab_strip_model()->GetActiveWebContents()->
    517               GetController()));
    518   LOG(INFO) << "Reloading.";
    519   chrome::Reload(browser(), CURRENT_TAB);
    520   reload_observer2.Wait();
    521   LOG(INFO) << "Reloading - done.";
    522   EXPECT_FALSE(process_map->Contains(
    523       contents->GetRenderProcessHost()->GetID()));
    524 }
    525 
    526 // Ensure that reloading a URL with JavaScript after installing or uninstalling
    527 // it as an app correctly swaps the process.  (http://crbug.com/80621)
    528 //
    529 // Crashes on Windows and Mac. http://crbug.com/238670
    530 // Added logging to help diagnose the location of the problem.
    531 IN_PROC_BROWSER_TEST_F(AppApiTest, ReloadIntoAppProcessWithJavaScript) {
    532   extensions::ProcessMap* process_map = extensions::ExtensionSystem::Get(
    533       browser()->profile())->extension_service()->process_map();
    534 
    535   host_resolver()->AddRule("*", "127.0.0.1");
    536   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
    537 
    538   // The app under test acts on URLs whose host is "localhost",
    539   // so the URLs we navigate to must have host "localhost".
    540   GURL base_url = GetTestBaseURL("app_process");
    541 
    542   // Load app, disable it, and navigate to the page.
    543   LOG(INFO) << "Loading extension.";
    544   const Extension* app =
    545       LoadExtension(test_data_dir_.AppendASCII("app_process"));
    546   LOG(INFO) << "Loading extension - done.";
    547   ASSERT_TRUE(app);
    548   LOG(INFO) << "Disabling extension.";
    549   DisableExtension(app->id());
    550   LOG(INFO) << "Disabling extension - done.";
    551   LOG(INFO) << "Navigate to path1/empty.html.";
    552   ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
    553   LOG(INFO) << "Navigate to path1/empty.html - done.";
    554   WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0);
    555   EXPECT_FALSE(process_map->Contains(
    556       contents->GetRenderProcessHost()->GetID()));
    557 
    558   // Enable app and reload via JavaScript.
    559   LOG(INFO) << "Enabling extension.";
    560   EnableExtension(app->id());
    561   LOG(INFO) << "Enabling extension - done.";
    562   content::WindowedNotificationObserver js_reload_observer(
    563       content::NOTIFICATION_LOAD_STOP,
    564       content::Source<NavigationController>(
    565           &browser()->tab_strip_model()->GetActiveWebContents()->
    566               GetController()));
    567   LOG(INFO) << "Executing location.reload().";
    568   ASSERT_TRUE(content::ExecuteScript(contents, "location.reload();"));
    569   js_reload_observer.Wait();
    570   LOG(INFO) << "Executing location.reload() - done.";
    571   EXPECT_TRUE(process_map->Contains(
    572       contents->GetRenderProcessHost()->GetID()));
    573 
    574   // Disable app and reload via JavaScript.
    575   LOG(INFO) << "Disabling extension.";
    576   DisableExtension(app->id());
    577   LOG(INFO) << "Disabling extension - done.";
    578   content::WindowedNotificationObserver js_reload_observer2(
    579       content::NOTIFICATION_LOAD_STOP,
    580       content::Source<NavigationController>(
    581           &browser()->tab_strip_model()->GetActiveWebContents()->
    582               GetController()));
    583   LOG(INFO) << "Executing location = location.";
    584   ASSERT_TRUE(content::ExecuteScript(contents, "location = location;"));
    585   js_reload_observer2.Wait();
    586   LOG(INFO) << "Executing location = location - done.";
    587   EXPECT_FALSE(process_map->Contains(
    588       contents->GetRenderProcessHost()->GetID()));
    589 }
    590 
    591 namespace {
    592 
    593 void RenderViewHostCreated(std::vector<content::RenderViewHost*>* rvh_vector,
    594                            content::RenderViewHost* rvh) {
    595   rvh_vector->push_back(rvh);
    596 }
    597 
    598 }  // namespace
    599 
    600 // Tests that if we have a non-app process (path3/container.html) that has an
    601 // iframe with  a URL in the app's extent (path1/iframe.html), then opening a
    602 // link from that iframe to a new window to a URL in the app's extent (path1/
    603 // empty.html) results in the new window being in an app process. See
    604 // http://crbug.com/89272 for more details.
    605 IN_PROC_BROWSER_TEST_F(AppApiTest, OpenAppFromIframe) {
    606 #if defined(OS_WIN) && defined(USE_ASH)
    607   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
    608   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
    609     return;
    610 #endif
    611 
    612   extensions::ProcessMap* process_map = extensions::ExtensionSystem::Get(
    613       browser()->profile())->extension_service()->process_map();
    614 
    615   host_resolver()->AddRule("*", "127.0.0.1");
    616   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
    617 
    618   GURL base_url = GetTestBaseURL("app_process");
    619 
    620   // Load app and start URL (not in the app).
    621   const Extension* app =
    622       LoadExtension(test_data_dir_.AppendASCII("app_process"));
    623   ASSERT_TRUE(app);
    624 
    625   std::vector<content::RenderViewHost*> rvh_vector;
    626   content::RenderViewHost::CreatedCallback rvh_callback(
    627       base::Bind(&RenderViewHostCreated, &rvh_vector));
    628   content::RenderViewHost::AddCreatedCallback(rvh_callback);
    629   ui_test_utils::NavigateToURL(browser(),
    630                                base_url.Resolve("path3/container.html"));
    631   content::RenderViewHost::RemoveCreatedCallback(rvh_callback);
    632   EXPECT_FALSE(process_map->Contains(
    633       browser()->tab_strip_model()->GetWebContentsAt(0)->
    634           GetRenderProcessHost()->GetID()));
    635 
    636   // Popup window should be in the app's process.
    637   ASSERT_EQ(3U, rvh_vector.size());
    638   RenderViewHost* popup_host = rvh_vector[2];
    639   EXPECT_TRUE(process_map->Contains(popup_host->GetProcess()->GetID()));
    640 }
    641 
    642 // Similar to the previous test, but ensure that popup blocking bypass
    643 // isn't granted to the iframe.  See crbug.com/117446.
    644 #if defined(OS_CHROMEOS)
    645 // http://crbug.com/153513
    646 #define MAYBE_OpenAppFromIframe DISABLED_OpenAppFromIframe
    647 #else
    648 #define MAYBE_OpenAppFromIframe OpenAppFromIframe
    649 #endif
    650 IN_PROC_BROWSER_TEST_F(BlockedAppApiTest, MAYBE_OpenAppFromIframe) {
    651   host_resolver()->AddRule("*", "127.0.0.1");
    652   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
    653 
    654   // Load app and start URL (not in the app).
    655   const Extension* app =
    656       LoadExtension(test_data_dir_.AppendASCII("app_process"));
    657   ASSERT_TRUE(app);
    658 
    659   ui_test_utils::NavigateToURL(
    660       browser(), GetTestBaseURL("app_process").Resolve("path3/container.html"));
    661 
    662   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
    663   BlockedContentTabHelper* blocked_content_tab_helper =
    664       BlockedContentTabHelper::FromWebContents(tab);
    665   PopupBlockerTabHelper* popup_blocker_tab_helper =
    666       PopupBlockerTabHelper::FromWebContents(tab);
    667   if (!blocked_content_tab_helper->GetBlockedContentsCount() &&
    668       (!popup_blocker_tab_helper ||
    669        !popup_blocker_tab_helper->GetBlockedPopupsCount())) {
    670     content::WindowedNotificationObserver observer(
    671         chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
    672         content::NotificationService::AllSources());
    673     observer.Wait();
    674   }
    675 
    676   EXPECT_EQ(1u,
    677             blocked_content_tab_helper->GetBlockedContentsCount() +
    678                 (popup_blocker_tab_helper
    679                      ? popup_blocker_tab_helper->GetBlockedPopupsCount()
    680                      : 0));
    681 }
    682 
    683 // Tests that if an extension launches an app via chrome.tabs.create with an URL
    684 // that's not in the app's extent but that server redirects to it, we still end
    685 // up with an app process. See http://crbug.com/99349 for more details.
    686 IN_PROC_BROWSER_TEST_F(AppApiTest, ServerRedirectToAppFromExtension) {
    687   host_resolver()->AddRule("*", "127.0.0.1");
    688   ASSERT_TRUE(StartEmbeddedTestServer());
    689 
    690   LoadExtension(test_data_dir_.AppendASCII("app_process"));
    691   const Extension* launcher =
    692       LoadExtension(test_data_dir_.AppendASCII("app_launcher"));
    693 
    694   // There should be two navigations by the time the app page is loaded.
    695   // 1. The extension launcher page.
    696   // 2. The app's URL (which includes a server redirect).
    697   // Note that the server redirect does not generate a navigation event.
    698   content::TestNavigationObserver test_navigation_observer(
    699       browser()->tab_strip_model()->GetActiveWebContents(),
    700       2);
    701   test_navigation_observer.StartWatchingNewWebContents();
    702 
    703   // Load the launcher extension, which should launch the app.
    704   ui_test_utils::NavigateToURLWithDisposition(
    705       browser(),
    706       launcher->GetResourceURL("server_redirect.html"),
    707       CURRENT_TAB,
    708       ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
    709 
    710   // Wait for app tab to be created and loaded.
    711   test_navigation_observer.WaitForObservation(
    712       base::Bind(&content::RunMessageLoop),
    713       base::Bind(&base::MessageLoop::Quit,
    714                  base::Unretained(base::MessageLoopForUI::current())));
    715 
    716   // App has loaded, and chrome.app.isInstalled should be true.
    717   bool is_installed = false;
    718   ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
    719       browser()->tab_strip_model()->GetActiveWebContents(),
    720       "window.domAutomationController.send(chrome.app.isInstalled)",
    721       &is_installed));
    722   ASSERT_TRUE(is_installed);
    723 }
    724 
    725 // Tests that if an extension launches an app via chrome.tabs.create with an URL
    726 // that's not in the app's extent but that client redirects to it, we still end
    727 // up with an app process.
    728 IN_PROC_BROWSER_TEST_F(AppApiTest, ClientRedirectToAppFromExtension) {
    729   host_resolver()->AddRule("*", "127.0.0.1");
    730   ASSERT_TRUE(StartEmbeddedTestServer());
    731 
    732   LoadExtension(test_data_dir_.AppendASCII("app_process"));
    733   const Extension* launcher =
    734       LoadExtension(test_data_dir_.AppendASCII("app_launcher"));
    735 
    736   // There should be three navigations by the time the app page is loaded.
    737   // 1. The extension launcher page.
    738   // 2. The URL that the extension launches, which client redirects.
    739   // 3. The app's URL.
    740   content::TestNavigationObserver test_navigation_observer(
    741       browser()->tab_strip_model()->GetActiveWebContents(),
    742       3);
    743   test_navigation_observer.StartWatchingNewWebContents();
    744 
    745   // Load the launcher extension, which should launch the app.
    746   ui_test_utils::NavigateToURLWithDisposition(
    747       browser(),
    748       launcher->GetResourceURL("client_redirect.html"),
    749       CURRENT_TAB,
    750       ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
    751 
    752   // Wait for app tab to be created and loaded.
    753   test_navigation_observer.WaitForObservation(
    754       base::Bind(&content::RunMessageLoop),
    755       base::Bind(&base::MessageLoop::Quit,
    756                  base::Unretained(base::MessageLoopForUI::current())));
    757 
    758   // App has loaded, and chrome.app.isInstalled should be true.
    759   bool is_installed = false;
    760   ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
    761       browser()->tab_strip_model()->GetActiveWebContents(),
    762       "window.domAutomationController.send(chrome.app.isInstalled)",
    763       &is_installed));
    764   ASSERT_TRUE(is_installed);
    765 }
    766 
    767 // Tests that if we have an app process (path1/container.html) with a non-app
    768 // iframe (path3/iframe.html), then opening a link from that iframe to a new
    769 // window to a same-origin non-app URL (path3/empty.html) should keep the window
    770 // in the app process.
    771 // This is in contrast to OpenAppFromIframe, since here the popup will not be
    772 // missing special permissions and should be scriptable from the iframe.
    773 // See http://crbug.com/92669 for more details.
    774 IN_PROC_BROWSER_TEST_F(AppApiTest, OpenWebPopupFromWebIframe) {
    775   extensions::ProcessMap* process_map = extensions::ExtensionSystem::Get(
    776       browser()->profile())->extension_service()->process_map();
    777 
    778   host_resolver()->AddRule("*", "127.0.0.1");
    779   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
    780 
    781   GURL base_url = GetTestBaseURL("app_process");
    782 
    783   // Load app and start URL (in the app).
    784   const Extension* app =
    785       LoadExtension(test_data_dir_.AppendASCII("app_process"));
    786   ASSERT_TRUE(app);
    787 
    788   std::vector<content::RenderViewHost*> rvh_vector;
    789   content::RenderViewHost::CreatedCallback rvh_callback(
    790       base::Bind(&RenderViewHostCreated, &rvh_vector));
    791   content::RenderViewHost::AddCreatedCallback(rvh_callback);
    792   ui_test_utils::NavigateToURL(browser(),
    793                                base_url.Resolve("path1/container.html"));
    794   content::RenderViewHost::RemoveCreatedCallback(rvh_callback);
    795   content::RenderProcessHost* process =
    796       browser()->tab_strip_model()->GetWebContentsAt(0)->GetRenderProcessHost();
    797   EXPECT_TRUE(process_map->Contains(process->GetID()));
    798 
    799   // Popup window should be in the app's process.
    800   ASSERT_EQ(2U, rvh_vector.size());
    801   RenderViewHost* popup_host = rvh_vector[1];
    802   EXPECT_EQ(process, popup_host->GetProcess());
    803 }
    804 
    805 // http://crbug.com/118502
    806 #if defined(OS_MACOSX) || defined(OS_LINUX)
    807 #define MAYBE_ReloadAppAfterCrash DISABLED_ReloadAppAfterCrash
    808 #else
    809 #define MAYBE_ReloadAppAfterCrash ReloadAppAfterCrash
    810 #endif
    811 IN_PROC_BROWSER_TEST_F(AppApiTest, MAYBE_ReloadAppAfterCrash) {
    812   extensions::ProcessMap* process_map = extensions::ExtensionSystem::Get(
    813       browser()->profile())->extension_service()->process_map();
    814 
    815   host_resolver()->AddRule("*", "127.0.0.1");
    816   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
    817 
    818   ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app_process")));
    819 
    820   GURL base_url = GetTestBaseURL("app_process");
    821 
    822   // Load the app, chrome.app.isInstalled should be true.
    823   ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
    824   WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0);
    825   EXPECT_TRUE(process_map->Contains(
    826       contents->GetRenderProcessHost()->GetID()));
    827   bool is_installed = false;
    828   ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
    829       contents,
    830       "window.domAutomationController.send(chrome.app.isInstalled)",
    831       &is_installed));
    832   ASSERT_TRUE(is_installed);
    833 
    834   // Crash the tab and reload it, chrome.app.isInstalled should still be true.
    835   content::CrashTab(browser()->tab_strip_model()->GetActiveWebContents());
    836   content::WindowedNotificationObserver observer(
    837       content::NOTIFICATION_LOAD_STOP,
    838       content::Source<NavigationController>(
    839           &browser()->tab_strip_model()->GetActiveWebContents()->
    840               GetController()));
    841   chrome::Reload(browser(), CURRENT_TAB);
    842   observer.Wait();
    843   ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
    844       contents,
    845       "window.domAutomationController.send(chrome.app.isInstalled)",
    846       &is_installed));
    847   ASSERT_TRUE(is_installed);
    848 }
    849