Home | History | Annotate | Download | only in signin
      1 // Copyright 2013 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/signin/signin_promo.h"
      7 #include "chrome/browser/ui/browser.h"
      8 #include "chrome/browser/ui/tabs/tab_strip_model.h"
      9 #include "chrome/browser/ui/webui/signin/inline_login_ui.h"
     10 #include "chrome/browser/ui/webui/signin/login_ui_service.h"
     11 #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
     12 #include "chrome/common/chrome_switches.h"
     13 #include "chrome/common/url_constants.h"
     14 #include "chrome/test/base/in_process_browser_test.h"
     15 #include "chrome/test/base/test_browser_window.h"
     16 #include "chrome/test/base/test_chrome_web_ui_controller_factory.h"
     17 #include "chrome/test/base/testing_browser_process.h"
     18 #include "chrome/test/base/ui_test_utils.h"
     19 #include "content/public/browser/render_frame_host.h"
     20 #include "content/public/browser/render_process_host.h"
     21 #include "content/public/browser/session_storage_namespace.h"
     22 #include "content/public/browser/storage_partition.h"
     23 #include "content/public/browser/web_contents.h"
     24 #include "content/public/browser/web_ui_controller.h"
     25 #include "content/public/common/url_constants.h"
     26 #include "content/public/test/browser_test_utils.h"
     27 #include "content/public/test/test_navigation_observer.h"
     28 #include "google_apis/gaia/fake_gaia.h"
     29 #include "google_apis/gaia/gaia_switches.h"
     30 #include "net/base/url_util.h"
     31 #include "net/test/embedded_test_server/embedded_test_server.h"
     32 #include "net/test/embedded_test_server/http_request.h"
     33 #include "net/test/embedded_test_server/http_response.h"
     34 #include "testing/gmock/include/gmock/gmock.h"
     35 #include "testing/gtest/include/gtest/gtest.h"
     36 
     37 using ::testing::_;
     38 using ::testing::Invoke;
     39 using ::testing::InvokeWithoutArgs;
     40 
     41 namespace {
     42 
     43 struct ContentInfo {
     44   ContentInfo(int pid, content::StoragePartition* storage_partition) {
     45     this->pid = pid;
     46     this->storage_partition = storage_partition;
     47   }
     48 
     49   int pid;
     50   content::StoragePartition* storage_partition;
     51 };
     52 
     53 ContentInfo NavigateAndGetInfo(
     54     Browser* browser,
     55     const GURL& url,
     56     WindowOpenDisposition disposition) {
     57   ui_test_utils::NavigateToURLWithDisposition(
     58       browser, url, disposition,
     59       ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
     60   content::WebContents* contents =
     61       browser->tab_strip_model()->GetActiveWebContents();
     62   content::RenderProcessHost* process = contents->GetRenderProcessHost();
     63   return ContentInfo(process->GetID(), process->GetStoragePartition());
     64 }
     65 
     66 // Returns a new WebUI object for the WebContents from |arg0|.
     67 ACTION(ReturnNewWebUI) {
     68   return new content::WebUIController(arg0);
     69 }
     70 
     71 // Mock the TestChromeWebUIControllerFactory::WebUIProvider to prove that we are
     72 // not called as expected.
     73 class FooWebUIProvider
     74     : public TestChromeWebUIControllerFactory::WebUIProvider {
     75  public:
     76   MOCK_METHOD2(NewWebUI, content::WebUIController*(content::WebUI* web_ui,
     77                                                    const GURL& url));
     78 };
     79 
     80 class MockLoginUIObserver : public LoginUIService::Observer {
     81  public:
     82   MOCK_METHOD0(OnUntrustedLoginUIShown, void());
     83 };
     84 
     85 const char kFooWebUIURL[] = "chrome://foo/";
     86 
     87 }  // namespace
     88 
     89 class InlineLoginUIBrowserTest : public InProcessBrowserTest {
     90  public:
     91   InlineLoginUIBrowserTest() {}
     92 };
     93 
     94 IN_PROC_BROWSER_TEST_F(InlineLoginUIBrowserTest, DifferentStorageId) {
     95   GURL test_url = ui_test_utils::GetTestUrl(
     96       base::FilePath(base::FilePath::kCurrentDirectory),
     97       base::FilePath(FILE_PATH_LITERAL("title1.html")));
     98 
     99   ContentInfo info1 =
    100       NavigateAndGetInfo(browser(), test_url, CURRENT_TAB);
    101   ContentInfo info2 =
    102       NavigateAndGetInfo(browser(),
    103                          signin::GetPromoURL(signin::SOURCE_START_PAGE, false),
    104                          CURRENT_TAB);
    105   NavigateAndGetInfo(browser(), test_url, CURRENT_TAB);
    106   ContentInfo info3 =
    107       NavigateAndGetInfo(browser(),
    108                          signin::GetPromoURL( signin::SOURCE_START_PAGE, false),
    109                          NEW_FOREGROUND_TAB);
    110 
    111   // The info for signin should be the same.
    112   ASSERT_EQ(info2.storage_partition, info3.storage_partition);
    113   // The info for test_url and signin should be different.
    114   ASSERT_NE(info1.storage_partition, info2.storage_partition);
    115 }
    116 
    117 IN_PROC_BROWSER_TEST_F(InlineLoginUIBrowserTest, OneProcessLimit) {
    118   GURL test_url_1 = ui_test_utils::GetTestUrl(
    119       base::FilePath(base::FilePath::kCurrentDirectory),
    120       base::FilePath(FILE_PATH_LITERAL("title1.html")));
    121   GURL test_url_2 = ui_test_utils::GetTestUrl(
    122       base::FilePath(base::FilePath::kCurrentDirectory),
    123       base::FilePath(FILE_PATH_LITERAL("data:text/html,Hello world!")));
    124 
    125   // Even when the process limit is set to one, the signin process should
    126   // still be given its own process and storage partition.
    127   content::RenderProcessHost::SetMaxRendererProcessCount(1);
    128 
    129   ContentInfo info1 =
    130       NavigateAndGetInfo(browser(), test_url_1, CURRENT_TAB);
    131   ContentInfo info2 =
    132       NavigateAndGetInfo(browser(), test_url_2, CURRENT_TAB);
    133   ContentInfo info3 =
    134       NavigateAndGetInfo(browser(),
    135                          signin::GetPromoURL( signin::SOURCE_START_PAGE, false),
    136                          CURRENT_TAB);
    137 
    138   ASSERT_EQ(info1.pid, info2.pid);
    139   ASSERT_NE(info1.pid, info3.pid);
    140 }
    141 
    142 class InlineLoginUISafeIframeBrowserTest : public InProcessBrowserTest {
    143  public:
    144   FooWebUIProvider& foo_provider() { return foo_provider_; }
    145 
    146   void WaitUntilUIReady() {
    147     content::DOMMessageQueue message_queue;
    148     ASSERT_TRUE(content::ExecuteScript(
    149         browser()->tab_strip_model()->GetActiveWebContents(),
    150         "if (!inline.login.getAuthExtHost())"
    151         "  inline.login.initialize();"
    152         "var handler = function() {"
    153         "  window.domAutomationController.setAutomationId(0);"
    154         "  window.domAutomationController.send('ready');"
    155         "};"
    156         "if (inline.login.isAuthReady())"
    157         "  handler();"
    158         "else"
    159         "  inline.login.getAuthExtHost().addEventListener('ready', handler);"));
    160 
    161     std::string message;
    162     do {
    163       ASSERT_TRUE(message_queue.WaitForMessage(&message));
    164     } while (message != "\"ready\"");
    165   }
    166 
    167  // Executes JavaScript code in the auth iframe hosted by gaia_auth extension.
    168   void ExecuteJsInSigninFrame(const std::string& js) {
    169     content::WebContents* web_contents =
    170         browser()->tab_strip_model()->GetActiveWebContents();
    171     ASSERT_TRUE(content::ExecuteScript(InlineLoginUI::GetAuthIframe(
    172         web_contents, GURL(), "signin-frame"), js));
    173   }
    174 
    175  private:
    176   virtual void SetUp() OVERRIDE {
    177     ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
    178 
    179     // EmbeddedTestServer spawns a thread to initialize socket.
    180     // Stop IO thread in preparation for fork and exec.
    181     embedded_test_server()->StopThread();
    182 
    183     InProcessBrowserTest::SetUp();
    184   }
    185 
    186   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
    187     const GURL& base_url = embedded_test_server()->base_url();
    188     command_line->AppendSwitchASCII(::switches::kGaiaUrl, base_url.spec());
    189     command_line->AppendSwitchASCII(::switches::kLsoUrl, base_url.spec());
    190     command_line->AppendSwitchASCII(::switches::kGoogleApisUrl,
    191                                     base_url.spec());
    192   }
    193 
    194   virtual void SetUpOnMainThread() OVERRIDE {
    195     embedded_test_server()->RestartThreadAndListen();
    196 
    197     content::WebUIControllerFactory::UnregisterFactoryForTesting(
    198         ChromeWebUIControllerFactory::GetInstance());
    199     test_factory_.reset(new TestChromeWebUIControllerFactory);
    200     content::WebUIControllerFactory::RegisterFactory(test_factory_.get());
    201     test_factory_->AddFactoryOverride(
    202         GURL(kFooWebUIURL).host(), &foo_provider_);
    203   }
    204 
    205   virtual void TearDownOnMainThread() OVERRIDE {
    206     test_factory_->RemoveFactoryOverride(GURL(kFooWebUIURL).host());
    207     content::WebUIControllerFactory::UnregisterFactoryForTesting(
    208         test_factory_.get());
    209     test_factory_.reset();
    210     EXPECT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
    211   }
    212 
    213   FooWebUIProvider foo_provider_;
    214   scoped_ptr<TestChromeWebUIControllerFactory> test_factory_;
    215 };
    216 
    217 // Make sure that the foo webui handler is working properly and that it gets
    218 // created when navigated to normally.
    219 IN_PROC_BROWSER_TEST_F(InlineLoginUISafeIframeBrowserTest, Basic) {
    220   const GURL kUrl(kFooWebUIURL);
    221   EXPECT_CALL(foo_provider(), NewWebUI(_, ::testing::Eq(kUrl)))
    222       .WillOnce(ReturnNewWebUI());
    223   ui_test_utils::NavigateToURL(browser(), GURL(kFooWebUIURL));
    224 }
    225 
    226 // Make sure that the foo webui handler does not get created when we try to
    227 // load it inside the iframe of the login ui.
    228 IN_PROC_BROWSER_TEST_F(InlineLoginUISafeIframeBrowserTest, NoWebUIInIframe) {
    229   GURL url = signin::GetPromoURL(signin::SOURCE_START_PAGE, false).
    230       Resolve("?source=0&frameUrl=chrome://foo");
    231   EXPECT_CALL(foo_provider(), NewWebUI(_, _)).Times(0);
    232   ui_test_utils::NavigateToURL(browser(), url);
    233 }
    234 
    235 // Flaky on CrOS, http://crbug.com/364759.
    236 #if defined(OS_CHROMEOS)
    237 #define MAYBE_TopFrameNavigationDisallowed DISABLED_TopFrameNavigationDisallowed
    238 #else
    239 #define MAYBE_TopFrameNavigationDisallowed TopFrameNavigationDisallowed
    240 #endif
    241 
    242 // Make sure that the gaia iframe cannot trigger top-frame navigation.
    243 // TODO(guohui): flaky on trybot crbug/364759.
    244 IN_PROC_BROWSER_TEST_F(InlineLoginUISafeIframeBrowserTest,
    245     MAYBE_TopFrameNavigationDisallowed) {
    246   // Loads into gaia iframe a web page that attempts to deframe on load.
    247   GURL deframe_url(embedded_test_server()->GetURL("/login/deframe.html"));
    248   GURL url(net::AppendOrReplaceQueryParameter(
    249       signin::GetPromoURL(signin::SOURCE_START_PAGE, false),
    250       "frameUrl", deframe_url.spec()));
    251   ui_test_utils::NavigateToURL(browser(), url);
    252   WaitUntilUIReady();
    253 
    254   content::WebContents* contents =
    255       browser()->tab_strip_model()->GetActiveWebContents();
    256   EXPECT_EQ(url, contents->GetVisibleURL());
    257 
    258   content::NavigationController& controller = contents->GetController();
    259   EXPECT_TRUE(controller.GetPendingEntry() == NULL);
    260 }
    261 
    262 // Flaky on CrOS, http://crbug.com/364759.
    263 #if defined(OS_CHROMEOS)
    264 #define MAYBE_NavigationToOtherChromeURLDisallowed \
    265     DISABLED_NavigationToOtherChromeURLDisallowed
    266 #else
    267 #define MAYBE_NavigationToOtherChromeURLDisallowed \
    268     NavigationToOtherChromeURLDisallowed
    269 #endif
    270 
    271 IN_PROC_BROWSER_TEST_F(InlineLoginUISafeIframeBrowserTest,
    272     MAYBE_NavigationToOtherChromeURLDisallowed) {
    273   ui_test_utils::NavigateToURL(
    274       browser(), signin::GetPromoURL(signin::SOURCE_START_PAGE, false));
    275   WaitUntilUIReady();
    276 
    277   content::WebContents* contents =
    278       browser()->tab_strip_model()->GetActiveWebContents();
    279   ASSERT_TRUE(content::ExecuteScript(
    280       contents, "window.location.href = 'chrome://foo'"));
    281 
    282   content::TestNavigationObserver navigation_observer(contents, 1);
    283   navigation_observer.Wait();
    284 
    285   EXPECT_EQ(GURL("about:blank"), contents->GetVisibleURL());
    286 }
    287 
    288 #if !defined(OS_CHROMEOS)
    289 IN_PROC_BROWSER_TEST_F(InlineLoginUISafeIframeBrowserTest,
    290     ConfirmationRequiredForNonsecureSignin) {
    291   FakeGaia fake_gaia;
    292   fake_gaia.Initialize();
    293 
    294   embedded_test_server()->RegisterRequestHandler(
    295       base::Bind(&FakeGaia::HandleRequest,
    296                  base::Unretained(&fake_gaia)));
    297   fake_gaia.SetFakeMergeSessionParams(
    298       "email", "fake-sid-cookie", "fake-lsid-cookie");
    299 
    300   // Navigates to the Chrome signin page which loads the fake gaia auth page.
    301   // Since the fake gaia auth page is served over HTTP, thus expects to see an
    302   // untrusted signin confirmation dialog upon submitting credentials below.
    303   ui_test_utils::NavigateToURL(
    304       browser(), signin::GetPromoURL(signin::SOURCE_START_PAGE, false));
    305   WaitUntilUIReady();
    306 
    307   MockLoginUIObserver observer;
    308   LoginUIServiceFactory::GetForProfile(browser()->profile())
    309       ->AddObserver(&observer);
    310   base::RunLoop run_loop;
    311   EXPECT_CALL(observer, OnUntrustedLoginUIShown())
    312       .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
    313 
    314   std::string js =
    315       "document.getElementById('Email').value = 'email';"
    316       "document.getElementById('Passwd').value = 'password';"
    317       "document.getElementById('signIn').click();";
    318   ExecuteJsInSigninFrame(js);
    319 
    320   run_loop.Run();
    321   base::MessageLoop::current()->RunUntilIdle();
    322 }
    323 #endif // OS_CHROMEOS
    324