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