// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/string_number_conversions.h" #include "base/utf_string_conversions.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_signin.h" #include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/browser/extensions/extension_test_message_listener.h" #include "chrome/browser/extensions/extension_webstore_private_api.h" #include "chrome/browser/net/gaia/token_service.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/sync/profile_sync_service.h" #include "chrome/browser/ui/browser.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/net/gaia/gaia_constants.h" #include "chrome/common/url_constants.h" #include "chrome/test/ui_test_utils.h" #include "content/browser/tab_contents/tab_contents.h" #include "googleurl/src/gurl.h" #include "net/base/mock_host_resolver.h" using chrome::kHttpScheme; using chrome::kStandardSchemeSeparator; namespace { const char kTestUrlHostname[] = "www.example.com"; } // namespace // A fake version of ProfileSyncService used for testing. class FakeProfileSyncService : public ProfileSyncService { public: FakeProfileSyncService() : ProfileSyncService(NULL, NULL, ""), setup_(false) { } virtual ~FakeProfileSyncService() {} // Overrides of virtual methods in ProfileSyncService. virtual bool HasSyncSetupCompleted() const { return setup_; } virtual void ChangePreferredDataTypes(const syncable::ModelTypeSet& types) { types_ = types; } virtual void GetPreferredDataTypes(syncable::ModelTypeSet* types) const { *types = types_; } virtual void SetSyncSetupCompleted() { setup_ = true; } private: bool setup_; syncable::ModelTypeSet types_; }; class FakeBrowserSignin : public BrowserSignin { public: // The |username_after_login| parameter determines what this fake // BrowserSignin will set the username to when ShowLoginDialog is called. FakeBrowserSignin(bool should_succeed, const std::string& initial_username, const std::string& username_after_login) : BrowserSignin(NULL), should_succeed_(should_succeed), username_(initial_username), username_after_login_(username_after_login) { } virtual ~FakeBrowserSignin() {} virtual std::string GetSignedInUsername() const { return username_; } virtual void RequestSignin(TabContents* tab_contents, const string16& preferred_email, const string16& message, SigninDelegate* delegate) { if (should_succeed_) { // Simulate valid login. username_ = username_after_login_; delegate->OnLoginSuccess(); // Fake a token available notification. Profile* profile = tab_contents->profile(); if (profile->IsOffTheRecord()) { profile = g_browser_process->profile_manager()->GetDefaultProfile(); } TokenService* token_service = profile->GetTokenService(); token_service->IssueAuthTokenForTest(GaiaConstants::kGaiaService, "new_token"); } else { delegate->OnLoginFailure(GoogleServiceAuthError( GoogleServiceAuthError::REQUEST_CANCELED)); } } private: bool should_succeed_; std::string username_; std::string username_after_login_; }; class ExtensionWebstorePrivateBrowserTest : public ExtensionBrowserTest { public: ExtensionWebstorePrivateBrowserTest() { test_url_base_ = std::string() + kHttpScheme + kStandardSchemeSeparator + kTestUrlHostname; } void SetUpCommandLine(CommandLine* command_line) { ExtensionBrowserTest::SetUpCommandLine(command_line); command_line->AppendSwitchASCII(switches::kAppsGalleryURL, test_url_base_); } // This generates a regular test server url pointing to a test file at // |relative_path|, but replaces the hostname with kTestUrlHostname so that // we get the webstore private APIs injected (this happens because of the // command line switch we added in SetupCommandLine). GURL GetUrl(const std::string& relative_path) { GURL base_url = test_server()->GetURL( "files/extensions/webstore_private/" + relative_path); GURL::Replacements replacements; std::string replacement_host = std::string(kTestUrlHostname); replacements.SetHostStr(replacement_host); return base_url.ReplaceComponents(replacements); } void RunLoginTestImpl(bool incognito, const std::string& relative_path, const std::string& initial_login, bool login_succeeds, const std::string& login_result) { // Clear the token service so previous tests don't affect things. TokenService* token_service = browser()->profile()->GetTokenService(); token_service->ResetCredentialsInMemory(); if (!initial_login.empty()) { // Initialize the token service with an existing token. token_service->IssueAuthTokenForTest(GaiaConstants::kGaiaService, "existing_token"); } FakeProfileSyncService sync_service; FakeBrowserSignin signin(login_succeeds, initial_login, login_result); WebstorePrivateApi::SetTestingProfileSyncService(&sync_service); WebstorePrivateApi::SetTestingBrowserSignin(&signin); ExtensionTestMessageListener listener("success", false); GURL url = GetUrl(relative_path); if (incognito) { ui_test_utils::OpenURLOffTheRecord(browser()->profile(), url); } else { ui_test_utils::NavigateToURL(browser(), url); } EXPECT_TRUE(listener.WaitUntilSatisfied()); WebstorePrivateApi::SetTestingBrowserSignin(NULL); WebstorePrivateApi::SetTestingProfileSyncService(NULL); } void RunLoginTest(const std::string& relative_path, const std::string& initial_login, bool login_succeeds, const std::string& login_result) { RunLoginTestImpl(true, relative_path, initial_login, login_succeeds, login_result); RunLoginTestImpl(false, relative_path, initial_login, login_succeeds, login_result); } protected: std::string test_url_base_; }; IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateBrowserTest, BrowserLogin) { host_resolver()->AddRule(kTestUrlHostname, "127.0.0.1"); ASSERT_TRUE(test_server()->Start()); RunLoginTest("browser_login/expect_nonempty.html", "foo@bar.com", false, ""); RunLoginTest("browser_login/prompt_no_preferred.html", "", true, ""); RunLoginTest("browser_login/prompt_preferred.html", "", true, "foo@bar.com"); RunLoginTest("browser_login/prompt_login_fails.html", "", false, "foo@bar.com"); }