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 on Linux.  http://crbug.com/341898
    380 #if defined(OS_LINUX)
    381 #define MAYBE_AppProcessRedirectBack DISABLED_AppProcessRedirectBack
    382 #else
    383 #define MAYBE_AppProcessRedirectBack AppProcessRedirectBack
    384 #endif
    385 IN_PROC_BROWSER_TEST_F(AppApiTest, MAYBE_AppProcessRedirectBack) {
    386   host_resolver()->AddRule("*", "127.0.0.1");
    387   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
    388 
    389   ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app_process")));
    390 
    391   // Open two tabs in the app.
    392   GURL base_url = GetTestBaseURL("app_process");
    393 
    394   chrome::NewTab(browser());
    395   ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
    396   chrome::NewTab(browser());
    397   // Wait until the second tab finishes its redirect train (2 hops).
    398   // 1. We navigate to redirect.html
    399   // 2. Renderer navigates and finishes, counting as a load stop.
    400   // 3. Renderer issues the meta refresh to navigate to server-redirect.
    401   // 4. Renderer is now in a "provisional load", waiting for navigation to
    402   //    complete.
    403   // 5. Browser sees a redirect response from server-redirect to empty.html, and
    404   //    transfers that to a new navigation, using RequestTransferURL.
    405   // 6. Renderer navigates to empty.html, and finishes loading, counting as the
    406   //    second load stop
    407   ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
    408       browser(), base_url.Resolve("path1/redirect.html"), 2);
    409 
    410   // 3 tabs, including the initial about:blank. The last 2 should be the same
    411   // process.
    412   ASSERT_EQ(3, browser()->tab_strip_model()->count());
    413   EXPECT_EQ("/extensions/api_test/app_process/path1/empty.html",
    414             browser()->tab_strip_model()->GetWebContentsAt(2)->
    415                 GetController().GetLastCommittedEntry()->GetURL().path());
    416   EXPECT_EQ(browser()->tab_strip_model()->GetWebContentsAt(1)->
    417                 GetRenderProcessHost(),
    418             browser()->tab_strip_model()->GetWebContentsAt(2)->
    419                 GetRenderProcessHost());
    420 }
    421 
    422 // Ensure that re-navigating to a URL after installing or uninstalling it as an
    423 // app correctly swaps the tab to the app process.  (http://crbug.com/80621)
    424 //
    425 // Fails on Windows. http://crbug.com/238670
    426 // Added logging to help diagnose the location of the problem.
    427 IN_PROC_BROWSER_TEST_F(AppApiTest, NavigateIntoAppProcess) {
    428   extensions::ProcessMap* process_map =
    429       extensions::ProcessMap::Get(browser()->profile());
    430 
    431   host_resolver()->AddRule("*", "127.0.0.1");
    432   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
    433 
    434   // The app under test acts on URLs whose host is "localhost",
    435   // so the URLs we navigate to must have host "localhost".
    436   GURL base_url = GetTestBaseURL("app_process");
    437 
    438   // Load an app URL before loading the app.
    439   LOG(INFO) << "Loading path1/empty.html.";
    440   ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
    441   LOG(INFO) << "Loading path1/empty.html - done.";
    442   WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0);
    443   EXPECT_FALSE(process_map->Contains(
    444       contents->GetRenderProcessHost()->GetID()));
    445 
    446   // Load app and re-navigate to the page.
    447   LOG(INFO) << "Loading extension.";
    448   const Extension* app =
    449       LoadExtension(test_data_dir_.AppendASCII("app_process"));
    450   LOG(INFO) << "Loading extension - done.";
    451   ASSERT_TRUE(app);
    452   LOG(INFO) << "Loading path1/empty.html.";
    453   ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
    454   LOG(INFO) << "Loading path1/empty.html - done.";
    455   EXPECT_TRUE(process_map->Contains(
    456       contents->GetRenderProcessHost()->GetID()));
    457 
    458   // Disable app and re-navigate to the page.
    459   LOG(INFO) << "Disabling extension.";
    460   DisableExtension(app->id());
    461   LOG(INFO) << "Disabling extension - done.";
    462   LOG(INFO) << "Loading path1/empty.html.";
    463   ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
    464   LOG(INFO) << "Loading path1/empty.html - done.";
    465   EXPECT_FALSE(process_map->Contains(
    466       contents->GetRenderProcessHost()->GetID()));
    467 }
    468 
    469 // Ensure that reloading a URL after installing or uninstalling it as an app
    470 // correctly swaps the tab to the app process.  (http://crbug.com/80621)
    471 //
    472 // Added logging to help diagnose the location of the problem.
    473 // http://crbug.com/238670
    474 IN_PROC_BROWSER_TEST_F(AppApiTest, ReloadIntoAppProcess) {
    475   extensions::ProcessMap* process_map =
    476       extensions::ProcessMap::Get(browser()->profile());
    477 
    478   host_resolver()->AddRule("*", "127.0.0.1");
    479   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
    480 
    481   // The app under test acts on URLs whose host is "localhost",
    482   // so the URLs we navigate to must have host "localhost".
    483   GURL base_url = GetTestBaseURL("app_process");
    484 
    485   // Load app, disable it, and navigate to the page.
    486   LOG(INFO) << "Loading extension.";
    487   const Extension* app =
    488       LoadExtension(test_data_dir_.AppendASCII("app_process"));
    489   LOG(INFO) << "Loading extension - done.";
    490   ASSERT_TRUE(app);
    491   LOG(INFO) << "Disabling extension.";
    492   DisableExtension(app->id());
    493   LOG(INFO) << "Disabling extension - done.";
    494   LOG(INFO) << "Navigate to path1/empty.html.";
    495   ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
    496   LOG(INFO) << "Navigate to path1/empty.html - done.";
    497   WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0);
    498   EXPECT_FALSE(process_map->Contains(
    499       contents->GetRenderProcessHost()->GetID()));
    500 
    501   // Enable app and reload the page.
    502   LOG(INFO) << "Enabling extension.";
    503   EnableExtension(app->id());
    504   LOG(INFO) << "Enabling extension - done.";
    505   content::WindowedNotificationObserver reload_observer(
    506       content::NOTIFICATION_LOAD_STOP,
    507       content::Source<NavigationController>(
    508           &browser()->tab_strip_model()->GetActiveWebContents()->
    509               GetController()));
    510   LOG(INFO) << "Reloading.";
    511   chrome::Reload(browser(), CURRENT_TAB);
    512   reload_observer.Wait();
    513   LOG(INFO) << "Reloading - done.";
    514   EXPECT_TRUE(process_map->Contains(
    515       contents->GetRenderProcessHost()->GetID()));
    516 
    517   // Disable app and reload the page.
    518   LOG(INFO) << "Disabling extension.";
    519   DisableExtension(app->id());
    520   LOG(INFO) << "Disabling extension - done.";
    521   content::WindowedNotificationObserver reload_observer2(
    522       content::NOTIFICATION_LOAD_STOP,
    523       content::Source<NavigationController>(
    524           &browser()->tab_strip_model()->GetActiveWebContents()->
    525               GetController()));
    526   LOG(INFO) << "Reloading.";
    527   chrome::Reload(browser(), CURRENT_TAB);
    528   reload_observer2.Wait();
    529   LOG(INFO) << "Reloading - done.";
    530   EXPECT_FALSE(process_map->Contains(
    531       contents->GetRenderProcessHost()->GetID()));
    532 }
    533 
    534 // Ensure that reloading a URL with JavaScript after installing or uninstalling
    535 // it as an app correctly swaps the process.  (http://crbug.com/80621)
    536 //
    537 // Crashes on Windows and Mac. http://crbug.com/238670
    538 // Added logging to help diagnose the location of the problem.
    539 IN_PROC_BROWSER_TEST_F(AppApiTest, ReloadIntoAppProcessWithJavaScript) {
    540   extensions::ProcessMap* process_map =
    541       extensions::ProcessMap::Get(browser()->profile());
    542 
    543   host_resolver()->AddRule("*", "127.0.0.1");
    544   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
    545 
    546   // The app under test acts on URLs whose host is "localhost",
    547   // so the URLs we navigate to must have host "localhost".
    548   GURL base_url = GetTestBaseURL("app_process");
    549 
    550   // Load app, disable it, and navigate to the page.
    551   LOG(INFO) << "Loading extension.";
    552   const Extension* app =
    553       LoadExtension(test_data_dir_.AppendASCII("app_process"));
    554   LOG(INFO) << "Loading extension - done.";
    555   ASSERT_TRUE(app);
    556   LOG(INFO) << "Disabling extension.";
    557   DisableExtension(app->id());
    558   LOG(INFO) << "Disabling extension - done.";
    559   LOG(INFO) << "Navigate to path1/empty.html.";
    560   ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
    561   LOG(INFO) << "Navigate to path1/empty.html - done.";
    562   WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0);
    563   EXPECT_FALSE(process_map->Contains(
    564       contents->GetRenderProcessHost()->GetID()));
    565 
    566   // Enable app and reload via JavaScript.
    567   LOG(INFO) << "Enabling extension.";
    568   EnableExtension(app->id());
    569   LOG(INFO) << "Enabling extension - done.";
    570   content::WindowedNotificationObserver js_reload_observer(
    571       content::NOTIFICATION_LOAD_STOP,
    572       content::Source<NavigationController>(
    573           &browser()->tab_strip_model()->GetActiveWebContents()->
    574               GetController()));
    575   LOG(INFO) << "Executing location.reload().";
    576   ASSERT_TRUE(content::ExecuteScript(contents, "location.reload();"));
    577   js_reload_observer.Wait();
    578   LOG(INFO) << "Executing location.reload() - done.";
    579   EXPECT_TRUE(process_map->Contains(
    580       contents->GetRenderProcessHost()->GetID()));
    581 
    582   // Disable app and reload via JavaScript.
    583   LOG(INFO) << "Disabling extension.";
    584   DisableExtension(app->id());
    585   LOG(INFO) << "Disabling extension - done.";
    586   content::WindowedNotificationObserver js_reload_observer2(
    587       content::NOTIFICATION_LOAD_STOP,
    588       content::Source<NavigationController>(
    589           &browser()->tab_strip_model()->GetActiveWebContents()->
    590               GetController()));
    591   LOG(INFO) << "Executing location = location.";
    592   ASSERT_TRUE(content::ExecuteScript(contents, "location = location;"));
    593   js_reload_observer2.Wait();
    594   LOG(INFO) << "Executing location = location - done.";
    595   EXPECT_FALSE(process_map->Contains(
    596       contents->GetRenderProcessHost()->GetID()));
    597 }
    598 
    599 // Tests that if we have a non-app process (path3/container.html) that has an
    600 // iframe with  a URL in the app's extent (path1/iframe.html), then opening a
    601 // link from that iframe to a new window to a URL in the app's extent (path1/
    602 // empty.html) results in the new window being in an app process. See
    603 // http://crbug.com/89272 for more details.
    604 IN_PROC_BROWSER_TEST_F(AppApiTest, OpenAppFromIframe) {
    605 #if defined(OS_WIN) && defined(USE_ASH)
    606   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
    607   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
    608     return;
    609 #endif
    610 
    611   extensions::ProcessMap* process_map =
    612       extensions::ProcessMap::Get(browser()->profile());
    613 
    614   host_resolver()->AddRule("*", "127.0.0.1");
    615   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
    616 
    617   GURL base_url = GetTestBaseURL("app_process");
    618 
    619   // Load app and start URL (not in the app).
    620   const Extension* app =
    621       LoadExtension(test_data_dir_.AppendASCII("app_process"));
    622   ASSERT_TRUE(app);
    623 
    624   ui_test_utils::NavigateToURL(browser(),
    625                                base_url.Resolve("path3/container.html"));
    626   EXPECT_FALSE(process_map->Contains(
    627       browser()->tab_strip_model()->GetWebContentsAt(0)->
    628           GetRenderProcessHost()->GetID()));
    629 
    630   const BrowserList* active_browser_list =
    631       BrowserList::GetInstance(chrome::GetActiveDesktop());
    632   EXPECT_EQ(2U, active_browser_list->size());
    633   content::WebContents* popup_contents =
    634       active_browser_list->get(1)->tab_strip_model()->GetActiveWebContents();
    635   content::WaitForLoadStop(popup_contents);
    636 
    637   // Popup window should be in the app's process.
    638   RenderViewHost* popup_host = popup_contents->GetRenderViewHost();
    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   PopupBlockerTabHelper* popup_blocker_tab_helper =
    664       PopupBlockerTabHelper::FromWebContents(tab);
    665   if (!popup_blocker_tab_helper->GetBlockedPopupsCount()) {
    666     content::WindowedNotificationObserver observer(
    667         chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
    668         content::NotificationService::AllSources());
    669     observer.Wait();
    670   }
    671 
    672   EXPECT_EQ(1u, popup_blocker_tab_helper->GetBlockedPopupsCount());
    673 }
    674 
    675 // Tests that if an extension launches an app via chrome.tabs.create with an URL
    676 // that's not in the app's extent but that server redirects to it, we still end
    677 // up with an app process. See http://crbug.com/99349 for more details.
    678 IN_PROC_BROWSER_TEST_F(AppApiTest, ServerRedirectToAppFromExtension) {
    679   host_resolver()->AddRule("*", "127.0.0.1");
    680   ASSERT_TRUE(StartEmbeddedTestServer());
    681 
    682   LoadExtension(test_data_dir_.AppendASCII("app_process"));
    683   const Extension* launcher =
    684       LoadExtension(test_data_dir_.AppendASCII("app_launcher"));
    685 
    686   // There should be two navigations by the time the app page is loaded.
    687   // 1. The extension launcher page.
    688   // 2. The app's URL (which includes a server redirect).
    689   // Note that the server redirect does not generate a navigation event.
    690   content::TestNavigationObserver test_navigation_observer(
    691       browser()->tab_strip_model()->GetActiveWebContents(),
    692       2);
    693   test_navigation_observer.StartWatchingNewWebContents();
    694 
    695   // Load the launcher extension, which should launch the app.
    696   ui_test_utils::NavigateToURLWithDisposition(
    697       browser(),
    698       launcher->GetResourceURL("server_redirect.html"),
    699       CURRENT_TAB,
    700       ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
    701 
    702   // Wait for app tab to be created and loaded.
    703   test_navigation_observer.Wait();
    704 
    705   // App has loaded, and chrome.app.isInstalled should be true.
    706   bool is_installed = false;
    707   ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
    708       browser()->tab_strip_model()->GetActiveWebContents(),
    709       "window.domAutomationController.send(chrome.app.isInstalled)",
    710       &is_installed));
    711   ASSERT_TRUE(is_installed);
    712 }
    713 
    714 // Tests that if an extension launches an app via chrome.tabs.create with an URL
    715 // that's not in the app's extent but that client redirects to it, we still end
    716 // up with an app process.
    717 IN_PROC_BROWSER_TEST_F(AppApiTest, ClientRedirectToAppFromExtension) {
    718   host_resolver()->AddRule("*", "127.0.0.1");
    719   ASSERT_TRUE(StartEmbeddedTestServer());
    720 
    721   LoadExtension(test_data_dir_.AppendASCII("app_process"));
    722   const Extension* launcher =
    723       LoadExtension(test_data_dir_.AppendASCII("app_launcher"));
    724 
    725   // There should be three navigations by the time the app page is loaded.
    726   // 1. The extension launcher page.
    727   // 2. The URL that the extension launches, which client redirects.
    728   // 3. The app's URL.
    729   content::TestNavigationObserver test_navigation_observer(
    730       browser()->tab_strip_model()->GetActiveWebContents(),
    731       3);
    732   test_navigation_observer.StartWatchingNewWebContents();
    733 
    734   // Load the launcher extension, which should launch the app.
    735   ui_test_utils::NavigateToURLWithDisposition(
    736       browser(),
    737       launcher->GetResourceURL("client_redirect.html"),
    738       CURRENT_TAB,
    739       ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
    740 
    741   // Wait for app tab to be created and loaded.
    742   test_navigation_observer.Wait();
    743 
    744   // App has loaded, and chrome.app.isInstalled should be true.
    745   bool is_installed = false;
    746   ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
    747       browser()->tab_strip_model()->GetActiveWebContents(),
    748       "window.domAutomationController.send(chrome.app.isInstalled)",
    749       &is_installed));
    750   ASSERT_TRUE(is_installed);
    751 }
    752 
    753 // Tests that if we have an app process (path1/container.html) with a non-app
    754 // iframe (path3/iframe.html), then opening a link from that iframe to a new
    755 // window to a same-origin non-app URL (path3/empty.html) should keep the window
    756 // in the app process.
    757 // This is in contrast to OpenAppFromIframe, since here the popup will not be
    758 // missing special permissions and should be scriptable from the iframe.
    759 // See http://crbug.com/92669 for more details.
    760 IN_PROC_BROWSER_TEST_F(AppApiTest, OpenWebPopupFromWebIframe) {
    761   extensions::ProcessMap* process_map =
    762       extensions::ProcessMap::Get(browser()->profile());
    763 
    764   host_resolver()->AddRule("*", "127.0.0.1");
    765   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
    766 
    767   GURL base_url = GetTestBaseURL("app_process");
    768 
    769   // Load app and start URL (in the app).
    770   const Extension* app =
    771       LoadExtension(test_data_dir_.AppendASCII("app_process"));
    772   ASSERT_TRUE(app);
    773 
    774   ui_test_utils::NavigateToURL(browser(),
    775                                base_url.Resolve("path1/container.html"));
    776   content::RenderProcessHost* process =
    777       browser()->tab_strip_model()->GetWebContentsAt(0)->GetRenderProcessHost();
    778   EXPECT_TRUE(process_map->Contains(process->GetID()));
    779 
    780   // Popup window should be in the app's process.
    781   const BrowserList* active_browser_list =
    782       BrowserList::GetInstance(chrome::GetActiveDesktop());
    783   EXPECT_EQ(2U, active_browser_list->size());
    784   content::WebContents* popup_contents =
    785       active_browser_list->get(1)->tab_strip_model()->GetActiveWebContents();
    786   content::WaitForLoadStop(popup_contents);
    787 
    788   RenderViewHost* popup_host = popup_contents->GetRenderViewHost();
    789   EXPECT_EQ(process, popup_host->GetProcess());
    790 }
    791 
    792 // http://crbug.com/118502
    793 #if defined(OS_MACOSX) || defined(OS_LINUX)
    794 #define MAYBE_ReloadAppAfterCrash DISABLED_ReloadAppAfterCrash
    795 #else
    796 #define MAYBE_ReloadAppAfterCrash ReloadAppAfterCrash
    797 #endif
    798 IN_PROC_BROWSER_TEST_F(AppApiTest, MAYBE_ReloadAppAfterCrash) {
    799   extensions::ProcessMap* process_map =
    800       extensions::ProcessMap::Get(browser()->profile());
    801 
    802   host_resolver()->AddRule("*", "127.0.0.1");
    803   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
    804 
    805   ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app_process")));
    806 
    807   GURL base_url = GetTestBaseURL("app_process");
    808 
    809   // Load the app, chrome.app.isInstalled should be true.
    810   ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
    811   WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0);
    812   EXPECT_TRUE(process_map->Contains(
    813       contents->GetRenderProcessHost()->GetID()));
    814   bool is_installed = false;
    815   ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
    816       contents,
    817       "window.domAutomationController.send(chrome.app.isInstalled)",
    818       &is_installed));
    819   ASSERT_TRUE(is_installed);
    820 
    821   // Crash the tab and reload it, chrome.app.isInstalled should still be true.
    822   content::CrashTab(browser()->tab_strip_model()->GetActiveWebContents());
    823   content::WindowedNotificationObserver observer(
    824       content::NOTIFICATION_LOAD_STOP,
    825       content::Source<NavigationController>(
    826           &browser()->tab_strip_model()->GetActiveWebContents()->
    827               GetController()));
    828   chrome::Reload(browser(), CURRENT_TAB);
    829   observer.Wait();
    830   ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
    831       contents,
    832       "window.domAutomationController.send(chrome.app.isInstalled)",
    833       &is_installed));
    834   ASSERT_TRUE(is_installed);
    835 }
    836 
    837 // Test that a cross-process navigation away from a hosted app stays in the same
    838 // BrowsingInstance, so that postMessage calls to the app's other windows still
    839 // work.
    840 IN_PROC_BROWSER_TEST_F(AppApiTest, SameBrowsingInstanceAfterSwap) {
    841   extensions::ProcessMap* process_map =
    842       extensions::ProcessMap::Get(browser()->profile());
    843 
    844   host_resolver()->AddRule("*", "127.0.0.1");
    845   ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
    846 
    847   GURL base_url = GetTestBaseURL("app_process");
    848 
    849   // Load app and start URL (in the app).
    850   const Extension* app =
    851       LoadExtension(test_data_dir_.AppendASCII("app_process"));
    852   ASSERT_TRUE(app);
    853 
    854   ui_test_utils::NavigateToURL(browser(),
    855                                base_url.Resolve("path1/iframe.html"));
    856   content::SiteInstance* app_instance =
    857       browser()->tab_strip_model()->GetWebContentsAt(0)->GetSiteInstance();
    858   EXPECT_TRUE(process_map->Contains(app_instance->GetProcess()->GetID()));
    859 
    860   // Popup window should be in the app's process.
    861   const BrowserList* active_browser_list =
    862       BrowserList::GetInstance(chrome::GetActiveDesktop());
    863   EXPECT_EQ(2U, active_browser_list->size());
    864   content::WebContents* popup_contents =
    865       active_browser_list->get(1)->tab_strip_model()->GetActiveWebContents();
    866   content::WaitForLoadStop(popup_contents);
    867 
    868   SiteInstance* popup_instance = popup_contents->GetSiteInstance();
    869   EXPECT_EQ(app_instance, popup_instance);
    870 
    871   // Navigate the popup to another process outside the app.
    872   GURL non_app_url(base_url.Resolve("path3/empty.html"));
    873   ui_test_utils::NavigateToURL(active_browser_list->get(1), non_app_url);
    874   SiteInstance* new_instance = popup_contents->GetSiteInstance();
    875   EXPECT_NE(app_instance, new_instance);
    876 
    877   // It should still be in the same BrowsingInstance, allowing postMessage to
    878   // work.
    879   EXPECT_TRUE(app_instance->IsRelatedSiteInstance(new_instance));
    880 }
    881