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/string_number_conversions.h"
6#include "base/utf_string_conversions.h"
7#include "chrome/browser/browser_process.h"
8#include "chrome/browser/browser_signin.h"
9#include "chrome/browser/extensions/extension_browsertest.h"
10#include "chrome/browser/extensions/extension_test_message_listener.h"
11#include "chrome/browser/extensions/extension_webstore_private_api.h"
12#include "chrome/browser/net/gaia/token_service.h"
13#include "chrome/browser/profiles/profile_manager.h"
14#include "chrome/browser/sync/profile_sync_service.h"
15#include "chrome/browser/ui/browser.h"
16#include "chrome/common/chrome_switches.h"
17#include "chrome/common/net/gaia/gaia_constants.h"
18#include "chrome/common/url_constants.h"
19#include "chrome/test/ui_test_utils.h"
20#include "content/browser/tab_contents/tab_contents.h"
21#include "googleurl/src/gurl.h"
22#include "net/base/mock_host_resolver.h"
23
24using chrome::kHttpScheme;
25using chrome::kStandardSchemeSeparator;
26
27namespace {
28
29const char kTestUrlHostname[] = "www.example.com";
30
31}  // namespace
32
33// A fake version of ProfileSyncService used for testing.
34class FakeProfileSyncService : public ProfileSyncService {
35 public:
36  FakeProfileSyncService()
37      : ProfileSyncService(NULL, NULL, ""),
38        setup_(false) {
39  }
40  virtual ~FakeProfileSyncService() {}
41
42  // Overrides of virtual methods in ProfileSyncService.
43  virtual bool HasSyncSetupCompleted() const {
44    return setup_;
45  }
46  virtual void ChangePreferredDataTypes(const syncable::ModelTypeSet& types) {
47    types_ = types;
48  }
49  virtual void GetPreferredDataTypes(syncable::ModelTypeSet* types) const {
50    *types = types_;
51  }
52  virtual void SetSyncSetupCompleted() {
53    setup_ = true;
54  }
55
56 private:
57  bool setup_;
58  syncable::ModelTypeSet types_;
59};
60
61class FakeBrowserSignin : public BrowserSignin {
62 public:
63  // The |username_after_login| parameter determines what this fake
64  // BrowserSignin will set the username to when ShowLoginDialog is called.
65  FakeBrowserSignin(bool should_succeed,
66                    const std::string& initial_username,
67                    const std::string& username_after_login)
68      : BrowserSignin(NULL),
69        should_succeed_(should_succeed),
70        username_(initial_username),
71        username_after_login_(username_after_login) {
72  }
73  virtual ~FakeBrowserSignin() {}
74
75  virtual std::string GetSignedInUsername() const {
76    return username_;
77  }
78
79  virtual void RequestSignin(TabContents* tab_contents,
80                             const string16& preferred_email,
81                             const string16& message,
82                             SigninDelegate* delegate) {
83    if (should_succeed_) {
84      // Simulate valid login.
85      username_ = username_after_login_;
86      delegate->OnLoginSuccess();
87
88      // Fake a token available notification.
89      Profile* profile = tab_contents->profile();
90      if (profile->IsOffTheRecord()) {
91        profile = g_browser_process->profile_manager()->GetDefaultProfile();
92      }
93      TokenService* token_service = profile->GetTokenService();
94      token_service->IssueAuthTokenForTest(GaiaConstants::kGaiaService,
95                                           "new_token");
96    } else {
97      delegate->OnLoginFailure(GoogleServiceAuthError(
98        GoogleServiceAuthError::REQUEST_CANCELED));
99    }
100  }
101
102 private:
103  bool should_succeed_;
104  std::string username_;
105  std::string username_after_login_;
106};
107
108class ExtensionWebstorePrivateBrowserTest : public ExtensionBrowserTest {
109 public:
110  ExtensionWebstorePrivateBrowserTest() {
111    test_url_base_ = std::string() + kHttpScheme + kStandardSchemeSeparator +
112                     kTestUrlHostname;
113  }
114
115  void SetUpCommandLine(CommandLine* command_line) {
116    ExtensionBrowserTest::SetUpCommandLine(command_line);
117    command_line->AppendSwitchASCII(switches::kAppsGalleryURL, test_url_base_);
118  }
119
120  // This generates a regular test server url pointing to a test file at
121  // |relative_path|, but replaces the hostname with kTestUrlHostname so that
122  // we get the webstore private APIs injected (this happens because of the
123  // command line switch we added in SetupCommandLine).
124  GURL GetUrl(const std::string& relative_path) {
125    GURL base_url = test_server()->GetURL(
126        "files/extensions/webstore_private/" + relative_path);
127    GURL::Replacements replacements;
128    std::string replacement_host = std::string(kTestUrlHostname);
129    replacements.SetHostStr(replacement_host);
130    return base_url.ReplaceComponents(replacements);
131  }
132
133  void RunLoginTestImpl(bool incognito,
134                        const std::string& relative_path,
135                        const std::string& initial_login,
136                        bool login_succeeds,
137                        const std::string& login_result) {
138    // Clear the token service so previous tests don't affect things.
139    TokenService* token_service = browser()->profile()->GetTokenService();
140    token_service->ResetCredentialsInMemory();
141    if (!initial_login.empty()) {
142      // Initialize the token service with an existing token.
143      token_service->IssueAuthTokenForTest(GaiaConstants::kGaiaService,
144                                           "existing_token");
145    }
146
147    FakeProfileSyncService sync_service;
148    FakeBrowserSignin signin(login_succeeds, initial_login, login_result);
149    WebstorePrivateApi::SetTestingProfileSyncService(&sync_service);
150    WebstorePrivateApi::SetTestingBrowserSignin(&signin);
151
152    ExtensionTestMessageListener listener("success", false);
153    GURL url = GetUrl(relative_path);
154    if (incognito) {
155      ui_test_utils::OpenURLOffTheRecord(browser()->profile(), url);
156    } else {
157      ui_test_utils::NavigateToURL(browser(), url);
158    }
159    EXPECT_TRUE(listener.WaitUntilSatisfied());
160
161    WebstorePrivateApi::SetTestingBrowserSignin(NULL);
162    WebstorePrivateApi::SetTestingProfileSyncService(NULL);
163  }
164
165  void RunLoginTest(const std::string& relative_path,
166                    const std::string& initial_login,
167                    bool login_succeeds,
168                    const std::string& login_result) {
169    RunLoginTestImpl(true,
170                     relative_path,
171                     initial_login,
172                     login_succeeds,
173                     login_result);
174    RunLoginTestImpl(false,
175                     relative_path,
176                     initial_login,
177                     login_succeeds,
178                     login_result);
179  }
180
181 protected:
182  std::string test_url_base_;
183};
184
185IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateBrowserTest, BrowserLogin) {
186  host_resolver()->AddRule(kTestUrlHostname, "127.0.0.1");
187  ASSERT_TRUE(test_server()->Start());
188
189  RunLoginTest("browser_login/expect_nonempty.html",
190               "foo@bar.com", false, "");
191
192  RunLoginTest("browser_login/prompt_no_preferred.html", "", true, "");
193
194  RunLoginTest("browser_login/prompt_preferred.html", "", true, "foo@bar.com");
195
196  RunLoginTest("browser_login/prompt_login_fails.html",
197               "", false, "foo@bar.com");
198}
199