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 "chrome/browser/ui/login/login_prompt.h"
6
7#include <string>
8
9#include "base/json/json_reader.h"
10#include "base/utf_string_conversions.h"
11#include "base/values.h"
12#include "chrome/browser/profiles/profile.h"
13#include "chrome/browser/ui/webui/chrome_url_data_manager.h"
14#include "chrome/browser/ui/webui/constrained_html_ui.h"
15#include "chrome/browser/ui/webui/html_dialog_ui.h"
16#include "chrome/common/jstemplate_builder.h"
17#include "chrome/common/url_constants.h"
18#include "content/browser/tab_contents/tab_contents.h"
19#include "grit/browser_resources.h"
20#include "grit/generated_resources.h"
21#include "ui/base/l10n/l10n_util.h"
22#include "ui/base/resource/resource_bundle.h"
23#include "ui/gfx/size.h"
24
25class LoginHandlerSource : public ChromeURLDataManager::DataSource {
26 public:
27  LoginHandlerSource()
28      : DataSource(chrome::kChromeUIHttpAuthHost, MessageLoop::current()) {}
29
30  virtual void StartDataRequest(const std::string& path,
31                                bool is_off_the_record,
32                                int request_id) {
33    DictionaryValue dict;
34    dict.SetString("username",
35                   l10n_util::GetStringUTF16(IDS_LOGIN_DIALOG_USERNAME_FIELD));
36    dict.SetString("password",
37                   l10n_util::GetStringUTF16(IDS_LOGIN_DIALOG_PASSWORD_FIELD));
38    dict.SetString("signin",
39                   l10n_util::GetStringUTF16(IDS_LOGIN_DIALOG_OK_BUTTON_LABEL));
40    dict.SetString("cancel", l10n_util::GetStringUTF16(IDS_CANCEL));
41
42    SetFontAndTextDirection(&dict);
43
44    const base::StringPiece html(
45        ResourceBundle::GetSharedInstance().GetRawDataResource(
46            IDR_HTTP_AUTH_HTML));
47    std::string response = jstemplate_builder::GetI18nTemplateHtml(html, &dict);
48
49    // Send the response.
50    scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes);
51    html_bytes->data.resize(response.size());
52    std::copy(response.begin(), response.end(), html_bytes->data.begin());
53    SendResponse(request_id, html_bytes);
54  }
55
56  virtual std::string GetMimeType(const std::string& path) const {
57    return "text/html";
58  }
59
60  static void RegisterDataSource(Profile *profile) {
61    ChromeURLDataManager* url_manager = profile->GetChromeURLDataManager();
62    LoginHandlerSource *source = new LoginHandlerSource();
63    url_manager->AddDataSource(source);
64  }
65
66 private:
67  virtual ~LoginHandlerSource() {}
68
69  DISALLOW_COPY_AND_ASSIGN(LoginHandlerSource);
70};
71
72class LoginHandlerHtml;
73
74class LoginHandlerHtmlDelegate : public HtmlDialogUIDelegate,
75                                 public WebUIMessageHandler {
76 public:
77  LoginHandlerHtmlDelegate(LoginHandlerHtml *login_handler,
78                           TabContents *tab_contents,
79                           const string16 explanation)
80      : login_handler_(login_handler),
81        tab_contents_(tab_contents),
82        explanation_(UTF16ToUTF8(explanation)),
83        closed_(false),
84        has_autofill_(false),
85        ready_for_autofill_(false) {
86  }
87
88  // HtmlDialogUIDelegate methods:
89  virtual bool IsDialogModal() const {
90    return true;
91  }
92
93  virtual std::wstring GetDialogTitle() const {
94    return UTF16ToWide(l10n_util::GetStringUTF16(IDS_LOGIN_DIALOG_TITLE));
95  }
96
97  virtual GURL GetDialogContentURL() const {
98    return GURL(chrome::kChromeUIHttpAuthURL);
99  }
100
101  virtual void GetWebUIMessageHandlers(
102      std::vector<WebUIMessageHandler*>* handlers) const {
103    const WebUIMessageHandler* handler = this;
104    handlers->push_back(const_cast<WebUIMessageHandler*>(handler));
105  }
106
107  virtual void GetDialogSize(gfx::Size* size) const {
108    size->set_width(kDialogWidth);
109    size->set_height(kDialogHeight);
110  }
111
112  virtual std::string GetDialogArgs() const {
113    return explanation_;
114  }
115
116  virtual void OnDialogClosed(const std::string& json_retval);
117
118  virtual void OnCloseContents(TabContents* source, bool* out_close_dialog) {}
119
120  virtual bool ShouldShowDialogTitle() const {
121    return true;
122  }
123
124  // WebUIMessageHandler method:
125  virtual void RegisterMessages() {
126    web_ui_->RegisterMessageCallback(
127        "GetAutofill",
128        NewCallback(this, &LoginHandlerHtmlDelegate::GetAutofill));
129  }
130
131  void ShowAutofillData(const std::wstring& username,
132                        const std::wstring& password);
133
134 private:
135  // Send autofill data to HTML once the dialog is ready and the data is
136  // available.
137  void SendAutofillData();
138
139  // Handle the request for autofill data from HTML.
140  void GetAutofill(const ListValue* args) {
141    ready_for_autofill_ = true;
142    SendAutofillData();
143  }
144
145  LoginHandlerHtml *login_handler_;
146  TabContents *tab_contents_;
147  std::string explanation_;
148  bool closed_;
149
150  bool has_autofill_;
151  bool ready_for_autofill_;
152  std::string autofill_username_;
153  std::string autofill_password_;
154
155  static const int kDialogWidth = 400;
156  static const int kDialogHeight = 130;
157
158  DISALLOW_COPY_AND_ASSIGN(LoginHandlerHtmlDelegate);
159};
160
161class LoginHandlerHtml : public LoginHandler {
162 public:
163  LoginHandlerHtml(net::AuthChallengeInfo* auth_info, net::URLRequest* request)
164      : LoginHandler(auth_info, request),
165        delegate_(NULL) {
166  }
167
168  // LoginModelObserver method:
169  virtual void OnAutofillDataAvailable(const std::wstring& username,
170                                       const std::wstring& password) {
171    if (delegate_)
172      delegate_->ShowAutofillData(username, password);
173  }
174
175  // LoginHandler method:
176  virtual void BuildViewForPasswordManager(PasswordManager* manager,
177                                           const string16& explanation);
178
179  friend class LoginHandlerHtmlDelegate;
180
181 protected:
182  virtual ~LoginHandlerHtml() {}
183
184 private:
185  LoginHandlerHtmlDelegate *delegate_;
186
187  void FreeAndRelease() {
188    SetDialog(NULL);
189    SetModel(NULL);
190    ReleaseSoon();
191  }
192
193  DISALLOW_COPY_AND_ASSIGN(LoginHandlerHtml);
194};
195
196void LoginHandlerHtmlDelegate::OnDialogClosed(const std::string& json_retval) {
197  if (closed_)
198    return;
199  closed_ = true;
200
201  scoped_ptr<Value> parsed_value(base::JSONReader::Read(json_retval, false));
202
203  if (!parsed_value.get() || !parsed_value->IsType(Value::TYPE_DICTIONARY)) {
204    login_handler_->CancelAuth();
205  } else {
206    DictionaryValue* res = static_cast<DictionaryValue*>(parsed_value.get());
207    string16 username;
208    string16 password;
209
210    if (!res->GetString("username", &username) ||
211        !res->GetString("password", &password)) {
212      login_handler_->CancelAuth();
213    } else {
214      login_handler_->SetAuth(username, password);
215    }
216  }
217
218  login_handler_->FreeAndRelease();
219}
220
221void LoginHandlerHtmlDelegate::ShowAutofillData(const std::wstring& username,
222                                                const std::wstring& password) {
223  autofill_username_ = WideToUTF8(username);
224  autofill_password_ = WideToUTF8(password);
225  has_autofill_ = true;
226  SendAutofillData();
227}
228
229void LoginHandlerHtmlDelegate::SendAutofillData() {
230  if (!closed_ && web_ui_ && has_autofill_ && ready_for_autofill_) {
231    StringValue username_v(autofill_username_);
232    StringValue password_v(autofill_password_);
233    web_ui_->CallJavascriptFunction("setAutofillCredentials",
234                                    username_v, password_v);
235  }
236}
237
238void LoginHandlerHtml::BuildViewForPasswordManager(
239    PasswordManager* manager, const string16& explanation) {
240  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
241
242  LOG(INFO) << "BuildViewForPasswordManager";
243
244  TabContents* tab_contents = GetTabContentsForLogin();
245  LoginHandlerSource::RegisterDataSource(tab_contents->profile());
246  delegate_ = new LoginHandlerHtmlDelegate(this, tab_contents, explanation);
247  ConstrainedWindow* dialog = ConstrainedHtmlUI::CreateConstrainedHtmlDialog(
248      tab_contents->profile(), delegate_, tab_contents);
249
250  SetModel(manager);
251  SetDialog(dialog);
252
253  NotifyAuthNeeded();
254}
255
256// static
257LoginHandler* LoginHandler::Create(net::AuthChallengeInfo* auth_info,
258                                   net::URLRequest* request) {
259  return new LoginHandlerHtml(auth_info, request);
260}
261