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 37using ::testing::_; 38using ::testing::Invoke; 39using ::testing::InvokeWithoutArgs; 40 41namespace { 42 43struct 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 53ContentInfo 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|. 67ACTION(ReturnNewWebUI) { 68 return new content::WebUIController(arg0); 69} 70 71// Mock the TestChromeWebUIControllerFactory::WebUIProvider to prove that we are 72// not called as expected. 73class FooWebUIProvider 74 : public TestChromeWebUIControllerFactory::WebUIProvider { 75 public: 76 MOCK_METHOD2(NewWebUI, content::WebUIController*(content::WebUI* web_ui, 77 const GURL& url)); 78}; 79 80class MockLoginUIObserver : public LoginUIService::Observer { 81 public: 82 MOCK_METHOD0(OnUntrustedLoginUIShown, void()); 83}; 84 85const char kFooWebUIURL[] = "chrome://foo/"; 86 87} // namespace 88 89class InlineLoginUIBrowserTest : public InProcessBrowserTest { 90 public: 91 InlineLoginUIBrowserTest() {} 92}; 93 94IN_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 117IN_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 142class 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. 219IN_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. 228IN_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. 244IN_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 271IN_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) 289IN_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