| // 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/browser_signin.h" |
| |
| #include <string> |
| #include <vector> |
| |
| #include "base/json/json_reader.h" |
| #include "base/json/json_writer.h" |
| #include "base/memory/singleton.h" |
| #include "base/message_loop.h" |
| #include "base/string_util.h" |
| #include "base/values.h" |
| #include "chrome/browser/prefs/pref_service.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/sync/profile_sync_service.h" |
| #include "chrome/browser/sync/sync_setup_flow.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/pref_names.h" |
| #include "chrome/common/url_constants.h" |
| #include "content/browser/browser_thread.h" |
| #include "content/browser/renderer_host/render_view_host.h" |
| #include "content/browser/tab_contents/tab_contents.h" |
| #include "content/common/notification_details.h" |
| #include "content/common/notification_source.h" |
| #include "content/common/notification_type.h" |
| #include "grit/browser_resources.h" |
| #include "ui/base/resource/resource_bundle.h" |
| |
| class BrowserSigninResourcesSource : public ChromeURLDataManager::DataSource { |
| public: |
| BrowserSigninResourcesSource() |
| : DataSource(chrome::kChromeUIDialogHost, MessageLoop::current()) { |
| } |
| |
| virtual void StartDataRequest(const std::string& path, |
| bool is_incognito, |
| int request_id); |
| |
| virtual std::string GetMimeType(const std::string& path) const { |
| return "text/html"; |
| } |
| |
| private: |
| virtual ~BrowserSigninResourcesSource() {} |
| |
| DISALLOW_COPY_AND_ASSIGN(BrowserSigninResourcesSource); |
| }; |
| |
| void BrowserSigninResourcesSource::StartDataRequest(const std::string& path, |
| bool is_incognito, |
| int request_id) { |
| const char kSigninPath[] = "signin"; |
| |
| std::string response; |
| if (path == kSigninPath) { |
| const base::StringPiece html( |
| ResourceBundle::GetSharedInstance().GetRawDataResource( |
| IDR_SIGNIN_HTML)); |
| DictionaryValue dict; |
| SetFontAndTextDirection(&dict); |
| response = jstemplate_builder::GetI18nTemplateHtml(html, &dict); |
| } |
| |
| 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); |
| } |
| |
| class BrowserSigninHtml : public HtmlDialogUIDelegate, |
| public WebUIMessageHandler { |
| public: |
| BrowserSigninHtml(BrowserSignin* signin, |
| const string16& suggested_email, |
| const string16& login_message); |
| virtual ~BrowserSigninHtml() {} |
| |
| // HtmlDialogUIDelegate implementation |
| virtual bool IsDialogModal() const { |
| return false; |
| }; |
| virtual std::wstring GetDialogTitle() const { |
| return L""; |
| } |
| virtual GURL GetDialogContentURL() const { |
| return GURL("chrome://dialog/signin"); |
| } |
| 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(600); |
| size->set_height(300); |
| } |
| virtual std::string GetDialogArgs() const { |
| return UTF16ToASCII(login_message_); |
| } |
| virtual void OnDialogClosed(const std::string& json_retval) { |
| closed_ = true; |
| signin_->Cancel(); |
| } |
| virtual void OnCloseContents(TabContents* source, bool* out_close_dialog) { |
| } |
| virtual bool ShouldShowDialogTitle() const { return true; } |
| |
| // WebUIMessageHandler implementation. |
| virtual void RegisterMessages(); |
| |
| // Refreshes the UI, such as after an authentication error. |
| void ReloadUI(); |
| |
| // Method which calls into javascript to force the dialog to close. |
| void ForceDialogClose(); |
| |
| private: |
| // JS callback handlers. |
| void HandleSigninInit(const ListValue* args); |
| void HandleSubmitAuth(const ListValue* args); |
| |
| // Nonowned pointer; |signin_| owns this object. |
| BrowserSignin* signin_; |
| |
| string16 suggested_email_; |
| string16 login_message_; |
| |
| bool closed_; |
| }; |
| |
| BrowserSigninHtml::BrowserSigninHtml(BrowserSignin* signin, |
| const string16& suggested_email, |
| const string16& login_message) |
| : signin_(signin), |
| suggested_email_(suggested_email), |
| login_message_(login_message), |
| closed_(false) { |
| } |
| |
| void BrowserSigninHtml::RegisterMessages() { |
| web_ui_->RegisterMessageCallback( |
| "SubmitAuth", NewCallback(this, &BrowserSigninHtml::HandleSubmitAuth)); |
| web_ui_->RegisterMessageCallback( |
| "SigninInit", NewCallback(this, &BrowserSigninHtml::HandleSigninInit)); |
| } |
| |
| void BrowserSigninHtml::ReloadUI() { |
| HandleSigninInit(NULL); |
| } |
| |
| void BrowserSigninHtml::ForceDialogClose() { |
| if (!closed_ && web_ui_) { |
| StringValue value("DialogClose"); |
| ListValue close_args; |
| close_args.Append(new StringValue("")); |
| web_ui_->CallJavascriptFunction("chrome.send", value, close_args); |
| } |
| } |
| |
| void BrowserSigninHtml::HandleSigninInit(const ListValue* args) { |
| if (!web_ui_) |
| return; |
| |
| RenderViewHost* rvh = web_ui_->tab_contents()->render_view_host(); |
| rvh->ExecuteJavascriptInWebFrame(ASCIIToUTF16("//iframe[@id='login']"), |
| ASCIIToUTF16("hideBlurb();")); |
| |
| DictionaryValue json_args; |
| std::string json; |
| std::wstring javascript(L""); |
| SyncSetupFlow::GetArgsForGaiaLogin(signin_->GetProfileSyncService(), |
| &json_args); |
| |
| // Replace the suggested email, unless sync has already required a |
| // particular value. |
| bool is_editable; |
| std::string user; |
| json_args.GetBoolean("editable_user", &is_editable); |
| json_args.GetString("user", &user); |
| if (is_editable && user.empty() && !suggested_email_.empty()) |
| json_args.SetString("user", suggested_email_); |
| |
| base::JSONWriter::Write(&json_args, false, &json); |
| javascript += L"showGaiaLogin(" + UTF8ToWide(json) + L");"; |
| rvh->ExecuteJavascriptInWebFrame(ASCIIToUTF16("//iframe[@id='login']"), |
| WideToUTF16Hack(javascript)); |
| } |
| |
| void BrowserSigninHtml::HandleSubmitAuth(const ListValue* args) { |
| std::string json; |
| if (!args->GetString(0, &json)) |
| NOTREACHED() << "Could not read JSON argument"; |
| |
| scoped_ptr<DictionaryValue> result(static_cast<DictionaryValue*>( |
| base::JSONReader::Read(json, false))); |
| std::string username; |
| std::string password; |
| std::string captcha; |
| std::string access_code; |
| if (!result.get() || |
| !result->GetString("user", &username) || |
| !result->GetString("pass", &password) || |
| !result->GetString("captcha", &captcha) || |
| !result->GetString("access_code", &access_code)) { |
| LOG(ERROR) << "Unintelligble format for authentication data from page."; |
| signin_->Cancel(); |
| } |
| signin_->GetProfileSyncService()->OnUserSubmittedAuth( |
| username, password, captcha, access_code); |
| } |
| |
| BrowserSignin::BrowserSignin(Profile* profile) |
| : profile_(profile), |
| delegate_(NULL), |
| html_dialog_ui_delegate_(NULL) { |
| // profile is NULL during testing. |
| if (profile) { |
| BrowserSigninResourcesSource* source = new BrowserSigninResourcesSource(); |
| profile->GetChromeURLDataManager()->AddDataSource(source); |
| } |
| } |
| |
| BrowserSignin::~BrowserSignin() { |
| delegate_ = NULL; |
| } |
| |
| void BrowserSignin::RequestSignin(TabContents* tab_contents, |
| const string16& suggested_email, |
| const string16& login_message, |
| SigninDelegate* delegate) { |
| CHECK(tab_contents); |
| CHECK(delegate); |
| // Cancel existing request. |
| if (delegate_) |
| Cancel(); |
| delegate_ = delegate; |
| suggested_email_ = suggested_email; |
| login_message_ = login_message; |
| RegisterAuthNotifications(); |
| ShowSigninTabModal(tab_contents); |
| } |
| |
| std::string BrowserSignin::GetSignedInUsername() const { |
| std::string username = |
| profile_->GetPrefs()->GetString(prefs::kGoogleServicesUsername); |
| VLOG(1) << "GetSignedInUsername: " << username; |
| return username; |
| } |
| |
| void BrowserSignin::Observe(NotificationType type, |
| const NotificationSource& source, |
| const NotificationDetails& details) { |
| switch (type.value) { |
| case NotificationType::GOOGLE_SIGNIN_SUCCESSFUL: { |
| VLOG(1) << "GOOGLE_SIGNIN_SUCCESSFUL"; |
| if (delegate_) |
| delegate_->OnLoginSuccess(); |
| // Close the dialog. |
| OnLoginFinished(); |
| break; |
| } |
| case NotificationType::GOOGLE_SIGNIN_FAILED: { |
| VLOG(1) << "GOOGLE_SIGNIN_FAILED"; |
| // The signin failed, refresh the UI with error information. |
| html_dialog_ui_delegate_->ReloadUI(); |
| break; |
| } |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| void BrowserSignin::Cancel() { |
| if (delegate_) { |
| delegate_->OnLoginFailure(GoogleServiceAuthError( |
| GoogleServiceAuthError::REQUEST_CANCELED)); |
| GetProfileSyncService()->OnUserCancelledDialog(); |
| } |
| OnLoginFinished(); |
| } |
| |
| void BrowserSignin::OnLoginFinished() { |
| if (html_dialog_ui_delegate_) |
| html_dialog_ui_delegate_->ForceDialogClose(); |
| // The dialog will be deleted by WebUI due to the dialog close, |
| // don't hold a reference. |
| html_dialog_ui_delegate_ = NULL; |
| |
| if (delegate_) { |
| UnregisterAuthNotifications(); |
| delegate_ = NULL; |
| } |
| } |
| |
| ProfileSyncService* BrowserSignin::GetProfileSyncService() const { |
| return profile_->GetProfileSyncService(); |
| } |
| |
| BrowserSigninHtml* BrowserSignin::CreateHtmlDialogUI() { |
| return new BrowserSigninHtml(this, suggested_email_, login_message_); |
| } |
| |
| void BrowserSignin::RegisterAuthNotifications() { |
| registrar_.Add(this, |
| NotificationType::GOOGLE_SIGNIN_SUCCESSFUL, |
| Source<Profile>(profile_)); |
| registrar_.Add(this, |
| NotificationType::GOOGLE_SIGNIN_FAILED, |
| Source<Profile>(profile_)); |
| } |
| |
| void BrowserSignin::UnregisterAuthNotifications() { |
| registrar_.Remove(this, |
| NotificationType::GOOGLE_SIGNIN_SUCCESSFUL, |
| Source<Profile>(profile_)); |
| registrar_.Remove(this, |
| NotificationType::GOOGLE_SIGNIN_FAILED, |
| Source<Profile>(profile_)); |
| } |
| |
| void BrowserSignin::ShowSigninTabModal(TabContents* tab_contents) { |
| // TODO(johnnyg): Need a linux views implementation for ConstrainedHtmlDialog. |
| #if defined(OS_WIN) || defined(OS_CHROMEOS) || !defined(TOOLKIT_VIEWS) |
| html_dialog_ui_delegate_ = CreateHtmlDialogUI(); |
| ConstrainedHtmlUI::CreateConstrainedHtmlDialog(profile_, |
| html_dialog_ui_delegate_, |
| tab_contents); |
| #endif |
| } |