Home | History | Annotate | Download | only in extensions
      1 // Copyright (c) 2011 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/utf_string_conversions.h"
      6 #include "chrome/browser/extensions/extension_apitest.h"
      7 #include "chrome/browser/extensions/extension_host.h"
      8 #include "chrome/browser/extensions/extension_process_manager.h"
      9 #include "chrome/browser/profiles/profile.h"
     10 #include "chrome/browser/ui/browser.h"
     11 #include "chrome/browser/ui/browser_list.h"
     12 #include "chrome/common/chrome_switches.h"
     13 #include "chrome/test/ui_test_utils.h"
     14 #include "content/browser/renderer_host/render_view_host.h"
     15 #include "content/browser/tab_contents/tab_contents.h"
     16 #include "net/base/mock_host_resolver.h"
     17 
     18 class AppApiTest : public ExtensionApiTest {
     19 };
     20 
     21 // Simulates a page calling window.open on an URL, and waits for the navigation.
     22 static void WindowOpenHelper(Browser* browser,
     23                              RenderViewHost* opener_host,
     24                              const GURL& url,
     25                              bool newtab_process_should_equal_opener) {
     26   ASSERT_TRUE(ui_test_utils::ExecuteJavaScript(
     27       opener_host, L"", L"window.open('" + UTF8ToWide(url.spec()) + L"');"));
     28 
     29   // The above window.open call is not user-initiated, it will create
     30   // a popup window instead of a new tab in current window.
     31   // Now the active tab in last active window should be the new tab.
     32   Browser* last_active_browser = BrowserList::GetLastActive();
     33   EXPECT_TRUE(last_active_browser);
     34   TabContents* newtab = last_active_browser->GetSelectedTabContents();
     35   EXPECT_TRUE(newtab);
     36   if (!newtab->controller().GetLastCommittedEntry() ||
     37       newtab->controller().GetLastCommittedEntry()->url() != url)
     38     ui_test_utils::WaitForNavigation(&newtab->controller());
     39   EXPECT_EQ(url, newtab->controller().GetLastCommittedEntry()->url());
     40   if (newtab_process_should_equal_opener)
     41     EXPECT_EQ(opener_host->process(), newtab->render_view_host()->process());
     42   else
     43     EXPECT_NE(opener_host->process(), newtab->render_view_host()->process());
     44 }
     45 
     46 // Simulates a page navigating itself to an URL, and waits for the navigation.
     47 static void NavigateTabHelper(TabContents* contents, const GURL& url) {
     48   bool result = false;
     49   ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
     50       contents->render_view_host(), L"",
     51       L"window.addEventListener('unload', function() {"
     52       L"    window.domAutomationController.send(true);"
     53       L"}, false);"
     54       L"window.location = '" + UTF8ToWide(url.spec()) + L"';",
     55       &result));
     56   ASSERT_TRUE(result);
     57 
     58   if (!contents->controller().GetLastCommittedEntry() ||
     59       contents->controller().GetLastCommittedEntry()->url() != url)
     60     ui_test_utils::WaitForNavigation(&contents->controller());
     61   EXPECT_EQ(url, contents->controller().GetLastCommittedEntry()->url());
     62 }
     63 
     64 IN_PROC_BROWSER_TEST_F(AppApiTest, AppProcess) {
     65   CommandLine::ForCurrentProcess()->AppendSwitch(
     66       switches::kDisablePopupBlocking);
     67 
     68   host_resolver()->AddRule("*", "127.0.0.1");
     69   ASSERT_TRUE(test_server()->Start());
     70 
     71   ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app_process")));
     72 
     73   // Open two tabs in the app, one outside it.
     74   GURL base_url = test_server()->GetURL(
     75       "files/extensions/api_test/app_process/");
     76 
     77   // The app under test acts on URLs whose host is "localhost",
     78   // so the URLs we navigate to must have host "localhost".
     79   GURL::Replacements replace_host;
     80   std::string host_str("localhost");  // must stay in scope with replace_host
     81   replace_host.SetHostStr(host_str);
     82   base_url = base_url.ReplaceComponents(replace_host);
     83 
     84   browser()->NewTab();
     85   ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
     86   browser()->NewTab();
     87   ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path2/empty.html"));
     88   browser()->NewTab();
     89   ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path3/empty.html"));
     90 
     91   // The extension should have opened 3 new tabs. Including the original blank
     92   // tab, we now have 4 tabs. Two should be part of the extension app, and
     93   // grouped in the same process.
     94   ASSERT_EQ(4, browser()->tab_count());
     95   RenderViewHost* host = browser()->GetTabContentsAt(1)->render_view_host();
     96 
     97   EXPECT_EQ(host->process(),
     98             browser()->GetTabContentsAt(2)->render_view_host()->process());
     99   EXPECT_NE(host->process(),
    100             browser()->GetTabContentsAt(3)->render_view_host()->process());
    101 
    102   // Now let's do the same using window.open. The same should happen.
    103   ASSERT_EQ(1u, BrowserList::GetBrowserCount(browser()->profile()));
    104   WindowOpenHelper(browser(), host,
    105                    base_url.Resolve("path1/empty.html"), true);
    106   WindowOpenHelper(browser(), host,
    107                    base_url.Resolve("path2/empty.html"), true);
    108   // TODO(creis): This should open in a new process (i.e., false for the last
    109   // argument), but we temporarily avoid swapping processes away from an app
    110   // until we're able to restore window.opener if the page later returns to an
    111   // in-app URL.  See crbug.com/65953.
    112   WindowOpenHelper(browser(), host,
    113                    base_url.Resolve("path3/empty.html"), true);
    114 
    115   // Now let's have these pages navigate, into or out of the extension web
    116   // extent. They should switch processes.
    117   const GURL& app_url(base_url.Resolve("path1/empty.html"));
    118   const GURL& non_app_url(base_url.Resolve("path3/empty.html"));
    119   NavigateTabHelper(browser()->GetTabContentsAt(2), non_app_url);
    120   NavigateTabHelper(browser()->GetTabContentsAt(3), app_url);
    121   // TODO(creis): This should swap out of the app's process (i.e., EXPECT_NE),
    122   // but we temporarily avoid swapping away from an app in case it needs to
    123   // communicate with window.opener later.  See crbug.com/65953.
    124   EXPECT_EQ(host->process(),
    125             browser()->GetTabContentsAt(2)->render_view_host()->process());
    126   EXPECT_EQ(host->process(),
    127             browser()->GetTabContentsAt(3)->render_view_host()->process());
    128 
    129   // If one of the popup tabs navigates back to the app, window.opener should
    130   // be valid.
    131   NavigateTabHelper(browser()->GetTabContentsAt(6), app_url);
    132   EXPECT_EQ(host->process(),
    133             browser()->GetTabContentsAt(6)->render_view_host()->process());
    134   bool windowOpenerValid = false;
    135   ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
    136       browser()->GetTabContentsAt(6)->render_view_host(), L"",
    137       L"window.domAutomationController.send(window.opener != null)",
    138       &windowOpenerValid));
    139   ASSERT_TRUE(windowOpenerValid);
    140 }
    141 
    142 // Tests that app process switching works properly in the following scenario:
    143 // 1. navigate to a page1 in the app
    144 // 2. page1 redirects to a page2 outside the app extent (ie, "/server-redirect")
    145 // 3. page2 redirects back to a page in the app
    146 // The final navigation should end up in the app process.
    147 // See http://crbug.com/61757
    148 IN_PROC_BROWSER_TEST_F(AppApiTest, AppProcessRedirectBack) {
    149   CommandLine::ForCurrentProcess()->AppendSwitch(
    150       switches::kDisablePopupBlocking);
    151 
    152   host_resolver()->AddRule("*", "127.0.0.1");
    153   ASSERT_TRUE(test_server()->Start());
    154 
    155   ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app_process")));
    156 
    157   // Open two tabs in the app.
    158   GURL base_url = test_server()->GetURL(
    159       "files/extensions/api_test/app_process/");
    160 
    161   // The app under test acts on URLs whose host is "localhost",
    162   // so the URLs we navigate to must have host "localhost".
    163   GURL::Replacements replace_host;
    164   std::string host_str("localhost");  // must stay in scope with replace_host
    165   replace_host.SetHostStr(host_str);
    166   base_url = base_url.ReplaceComponents(replace_host);
    167 
    168   browser()->NewTab();
    169   ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html"));
    170   browser()->NewTab();
    171   // Wait until the second tab finishes its redirect train (2 hops).
    172   ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
    173       browser(), base_url.Resolve("path1/redirect.html"), 2);
    174 
    175   // 3 tabs, including the initial about:blank. The last 2 should be the same
    176   // process.
    177   ASSERT_EQ(3, browser()->tab_count());
    178   EXPECT_EQ("/files/extensions/api_test/app_process/path1/empty.html",
    179             browser()->GetTabContentsAt(2)->controller().
    180                 GetLastCommittedEntry()->url().path());
    181   RenderViewHost* host = browser()->GetTabContentsAt(1)->render_view_host();
    182   EXPECT_EQ(host->process(),
    183             browser()->GetTabContentsAt(2)->render_view_host()->process());
    184 }
    185