// 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 "chrome/browser/ui/login/login_prompt.h"
#include <string>
#include "base/json/json_reader.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/chrome_url_data_manager.h"
#include "chrome/browser/ui/webui/constrained_html_ui.h"
#include "chrome/browser/ui/webui/html_dialog_ui.h"
#include "chrome/common/jstemplate_builder.h"
#include "chrome/common/url_constants.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "grit/browser_resources.h"
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/gfx/size.h"
class LoginHandlerSource : public ChromeURLDataManager::DataSource {
public:
LoginHandlerSource()
: DataSource(chrome::kChromeUIHttpAuthHost, MessageLoop::current()) {}
virtual void StartDataRequest(const std::string& path,
bool is_off_the_record,
int request_id) {
DictionaryValue dict;
dict.SetString("username",
l10n_util::GetStringUTF16(IDS_LOGIN_DIALOG_USERNAME_FIELD));
dict.SetString("password",
l10n_util::GetStringUTF16(IDS_LOGIN_DIALOG_PASSWORD_FIELD));
dict.SetString("signin",
l10n_util::GetStringUTF16(IDS_LOGIN_DIALOG_OK_BUTTON_LABEL));
dict.SetString("cancel", l10n_util::GetStringUTF16(IDS_CANCEL));
SetFontAndTextDirection(&dict);
const base::StringPiece html(
ResourceBundle::GetSharedInstance().GetRawDataResource(
IDR_HTTP_AUTH_HTML));
std::string response = jstemplate_builder::GetI18nTemplateHtml(html, &dict);
// Send the response.
scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes);
html_bytes->data.resize(response.size());
std::copy(response.begin(), response.end(), html_bytes->data.begin());
SendResponse(request_id, html_bytes);
}
virtual std::string GetMimeType(const std::string& path) const {
return "text/html";
}
static void RegisterDataSource(Profile *profile) {
ChromeURLDataManager* url_manager = profile->GetChromeURLDataManager();
LoginHandlerSource *source = new LoginHandlerSource();
url_manager->AddDataSource(source);
}
private:
virtual ~LoginHandlerSource() {}
DISALLOW_COPY_AND_ASSIGN(LoginHandlerSource);
};
class LoginHandlerHtml;
class LoginHandlerHtmlDelegate : public HtmlDialogUIDelegate,
public WebUIMessageHandler {
public:
LoginHandlerHtmlDelegate(LoginHandlerHtml *login_handler,
TabContents *tab_contents,
const string16 explanation)
: login_handler_(login_handler),
tab_contents_(tab_contents),
explanation_(UTF16ToUTF8(explanation)),
closed_(false),
has_autofill_(false),
ready_for_autofill_(false) {
}
// HtmlDialogUIDelegate methods:
virtual bool IsDialogModal() const {
return true;
}
virtual std::wstring GetDialogTitle() const {
return UTF16ToWide(l10n_util::GetStringUTF16(IDS_LOGIN_DIALOG_TITLE));
}
virtual GURL GetDialogContentURL() const {
return GURL(chrome::kChromeUIHttpAuthURL);
}
virtual void GetWebUIMessageHandlers(
std::vector<WebUIMessageHandler*>* handlers) const {
const WebUIMessageHandler* handler = this;
handlers->push_back(const_cast<WebUIMessageHandler*>(handler));
}
virtual void GetDialogSize(gfx::Size* size) const {
size->set_width(kDialogWidth);
size->set_height(kDialogHeight);
}
virtual std::string GetDialogArgs() const {
return explanation_;
}
virtual void OnDialogClosed(const std::string& json_retval);
virtual void OnCloseContents(TabContents* source, bool* out_close_dialog) {}
virtual bool ShouldShowDialogTitle() const {
return true;
}
// WebUIMessageHandler method:
virtual void RegisterMessages() {
web_ui_->RegisterMessageCallback(
"GetAutofill",
NewCallback(this, &LoginHandlerHtmlDelegate::GetAutofill));
}
void ShowAutofillData(const std::wstring& username,
const std::wstring& password);
private:
// Send autofill data to HTML once the dialog is ready and the data is
// available.
void SendAutofillData();
// Handle the request for autofill data from HTML.
void GetAutofill(const ListValue* args) {
ready_for_autofill_ = true;
SendAutofillData();
}
LoginHandlerHtml *login_handler_;
TabContents *tab_contents_;
std::string explanation_;
bool closed_;
bool has_autofill_;
bool ready_for_autofill_;
std::string autofill_username_;
std::string autofill_password_;
static const int kDialogWidth = 400;
static const int kDialogHeight = 130;
DISALLOW_COPY_AND_ASSIGN(LoginHandlerHtmlDelegate);
};
class LoginHandlerHtml : public LoginHandler {
public:
LoginHandlerHtml(net::AuthChallengeInfo* auth_info, net::URLRequest* request)
: LoginHandler(auth_info, request),
delegate_(NULL) {
}
// LoginModelObserver method:
virtual void OnAutofillDataAvailable(const std::wstring& username,
const std::wstring& password) {
if (delegate_)
delegate_->ShowAutofillData(username, password);
}
// LoginHandler method:
virtual void BuildViewForPasswordManager(PasswordManager* manager,
const string16& explanation);
friend class LoginHandlerHtmlDelegate;
protected:
virtual ~LoginHandlerHtml() {}
private:
LoginHandlerHtmlDelegate *delegate_;
void FreeAndRelease() {
SetDialog(NULL);
SetModel(NULL);
ReleaseSoon();
}
DISALLOW_COPY_AND_ASSIGN(LoginHandlerHtml);
};
void LoginHandlerHtmlDelegate::OnDialogClosed(const std::string& json_retval) {
if (closed_)
return;
closed_ = true;
scoped_ptr<Value> parsed_value(base::JSONReader::Read(json_retval, false));
if (!parsed_value.get() || !parsed_value->IsType(Value::TYPE_DICTIONARY)) {
login_handler_->CancelAuth();
} else {
DictionaryValue* res = static_cast<DictionaryValue*>(parsed_value.get());
string16 username;
string16 password;
if (!res->GetString("username", &username) ||
!res->GetString("password", &password)) {
login_handler_->CancelAuth();
} else {
login_handler_->SetAuth(username, password);
}
}
login_handler_->FreeAndRelease();
}
void LoginHandlerHtmlDelegate::ShowAutofillData(const std::wstring& username,
const std::wstring& password) {
autofill_username_ = WideToUTF8(username);
autofill_password_ = WideToUTF8(password);
has_autofill_ = true;
SendAutofillData();
}
void LoginHandlerHtmlDelegate::SendAutofillData() {
if (!closed_ && web_ui_ && has_autofill_ && ready_for_autofill_) {
StringValue username_v(autofill_username_);
StringValue password_v(autofill_password_);
web_ui_->CallJavascriptFunction("setAutofillCredentials",
username_v, password_v);
}
}
void LoginHandlerHtml::BuildViewForPasswordManager(
PasswordManager* manager, const string16& explanation) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
LOG(INFO) << "BuildViewForPasswordManager";
TabContents* tab_contents = GetTabContentsForLogin();
LoginHandlerSource::RegisterDataSource(tab_contents->profile());
delegate_ = new LoginHandlerHtmlDelegate(this, tab_contents, explanation);
ConstrainedWindow* dialog = ConstrainedHtmlUI::CreateConstrainedHtmlDialog(
tab_contents->profile(), delegate_, tab_contents);
SetModel(manager);
SetDialog(dialog);
NotifyAuthNeeded();
}
// static
LoginHandler* LoginHandler::Create(net::AuthChallengeInfo* auth_info,
net::URLRequest* request) {
return new LoginHandlerHtml(auth_info, request);
}