Home | History | Annotate | Download | only in browser
      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/browser_signin.h"
      6 
      7 #include <string>
      8 #include <vector>
      9 
     10 #include "base/json/json_reader.h"
     11 #include "base/json/json_writer.h"
     12 #include "base/memory/singleton.h"
     13 #include "base/message_loop.h"
     14 #include "base/string_util.h"
     15 #include "base/values.h"
     16 #include "chrome/browser/prefs/pref_service.h"
     17 #include "chrome/browser/profiles/profile.h"
     18 #include "chrome/browser/sync/profile_sync_service.h"
     19 #include "chrome/browser/sync/sync_setup_flow.h"
     20 #include "chrome/browser/ui/webui/chrome_url_data_manager.h"
     21 #include "chrome/browser/ui/webui/constrained_html_ui.h"
     22 #include "chrome/browser/ui/webui/html_dialog_ui.h"
     23 #include "chrome/common/jstemplate_builder.h"
     24 #include "chrome/common/pref_names.h"
     25 #include "chrome/common/url_constants.h"
     26 #include "content/browser/browser_thread.h"
     27 #include "content/browser/renderer_host/render_view_host.h"
     28 #include "content/browser/tab_contents/tab_contents.h"
     29 #include "content/common/notification_details.h"
     30 #include "content/common/notification_source.h"
     31 #include "content/common/notification_type.h"
     32 #include "grit/browser_resources.h"
     33 #include "ui/base/resource/resource_bundle.h"
     34 
     35 class BrowserSigninResourcesSource : public ChromeURLDataManager::DataSource {
     36  public:
     37   BrowserSigninResourcesSource()
     38       : DataSource(chrome::kChromeUIDialogHost, MessageLoop::current()) {
     39   }
     40 
     41   virtual void StartDataRequest(const std::string& path,
     42                                 bool is_incognito,
     43                                 int request_id);
     44 
     45   virtual std::string GetMimeType(const std::string& path) const {
     46     return "text/html";
     47   }
     48 
     49  private:
     50   virtual ~BrowserSigninResourcesSource() {}
     51 
     52   DISALLOW_COPY_AND_ASSIGN(BrowserSigninResourcesSource);
     53 };
     54 
     55 void BrowserSigninResourcesSource::StartDataRequest(const std::string& path,
     56                                                     bool is_incognito,
     57                                                     int request_id) {
     58   const char kSigninPath[] = "signin";
     59 
     60   std::string response;
     61   if (path == kSigninPath) {
     62     const base::StringPiece html(
     63         ResourceBundle::GetSharedInstance().GetRawDataResource(
     64             IDR_SIGNIN_HTML));
     65     DictionaryValue dict;
     66     SetFontAndTextDirection(&dict);
     67     response = jstemplate_builder::GetI18nTemplateHtml(html, &dict);
     68   }
     69 
     70   scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes);
     71   html_bytes->data.resize(response.size());
     72   std::copy(response.begin(), response.end(), html_bytes->data.begin());
     73   SendResponse(request_id, html_bytes);
     74 }
     75 
     76 class BrowserSigninHtml : public HtmlDialogUIDelegate,
     77                           public WebUIMessageHandler {
     78  public:
     79   BrowserSigninHtml(BrowserSignin* signin,
     80                     const string16& suggested_email,
     81                     const string16& login_message);
     82   virtual ~BrowserSigninHtml() {}
     83 
     84   // HtmlDialogUIDelegate implementation
     85   virtual bool IsDialogModal() const {
     86     return false;
     87   };
     88   virtual std::wstring GetDialogTitle() const {
     89     return L"";
     90   }
     91   virtual GURL GetDialogContentURL() const {
     92     return GURL("chrome://dialog/signin");
     93   }
     94   virtual void GetWebUIMessageHandlers(
     95       std::vector<WebUIMessageHandler*>* handlers) const {
     96     const WebUIMessageHandler* handler = this;
     97     handlers->push_back(const_cast<WebUIMessageHandler*>(handler));
     98   }
     99   virtual void GetDialogSize(gfx::Size* size) const {
    100     size->set_width(600);
    101     size->set_height(300);
    102   }
    103   virtual std::string GetDialogArgs() const {
    104     return UTF16ToASCII(login_message_);
    105   }
    106   virtual void OnDialogClosed(const std::string& json_retval) {
    107     closed_ = true;
    108     signin_->Cancel();
    109   }
    110   virtual void OnCloseContents(TabContents* source, bool* out_close_dialog) {
    111   }
    112   virtual bool ShouldShowDialogTitle() const { return true; }
    113 
    114   // WebUIMessageHandler implementation.
    115   virtual void RegisterMessages();
    116 
    117   // Refreshes the UI, such as after an authentication error.
    118   void ReloadUI();
    119 
    120   // Method which calls into javascript to force the dialog to close.
    121   void ForceDialogClose();
    122 
    123  private:
    124   // JS callback handlers.
    125   void HandleSigninInit(const ListValue* args);
    126   void HandleSubmitAuth(const ListValue* args);
    127 
    128   // Nonowned pointer; |signin_| owns this object.
    129   BrowserSignin* signin_;
    130 
    131   string16 suggested_email_;
    132   string16 login_message_;
    133 
    134   bool closed_;
    135 };
    136 
    137 BrowserSigninHtml::BrowserSigninHtml(BrowserSignin* signin,
    138                                      const string16& suggested_email,
    139                                      const string16& login_message)
    140     : signin_(signin),
    141       suggested_email_(suggested_email),
    142       login_message_(login_message),
    143       closed_(false) {
    144 }
    145 
    146 void BrowserSigninHtml::RegisterMessages() {
    147   web_ui_->RegisterMessageCallback(
    148       "SubmitAuth", NewCallback(this, &BrowserSigninHtml::HandleSubmitAuth));
    149   web_ui_->RegisterMessageCallback(
    150       "SigninInit", NewCallback(this, &BrowserSigninHtml::HandleSigninInit));
    151 }
    152 
    153 void BrowserSigninHtml::ReloadUI() {
    154   HandleSigninInit(NULL);
    155 }
    156 
    157 void BrowserSigninHtml::ForceDialogClose() {
    158   if (!closed_ && web_ui_) {
    159     StringValue value("DialogClose");
    160     ListValue close_args;
    161     close_args.Append(new StringValue(""));
    162     web_ui_->CallJavascriptFunction("chrome.send", value, close_args);
    163   }
    164 }
    165 
    166 void BrowserSigninHtml::HandleSigninInit(const ListValue* args) {
    167   if (!web_ui_)
    168     return;
    169 
    170   RenderViewHost* rvh = web_ui_->tab_contents()->render_view_host();
    171   rvh->ExecuteJavascriptInWebFrame(ASCIIToUTF16("//iframe[@id='login']"),
    172                                    ASCIIToUTF16("hideBlurb();"));
    173 
    174   DictionaryValue json_args;
    175   std::string json;
    176   std::wstring javascript(L"");
    177   SyncSetupFlow::GetArgsForGaiaLogin(signin_->GetProfileSyncService(),
    178                                      &json_args);
    179 
    180   // Replace the suggested email, unless sync has already required a
    181   // particular value.
    182   bool is_editable;
    183   std::string user;
    184   json_args.GetBoolean("editable_user", &is_editable);
    185   json_args.GetString("user", &user);
    186   if (is_editable && user.empty() && !suggested_email_.empty())
    187     json_args.SetString("user", suggested_email_);
    188 
    189   base::JSONWriter::Write(&json_args, false, &json);
    190   javascript += L"showGaiaLogin(" + UTF8ToWide(json) + L");";
    191   rvh->ExecuteJavascriptInWebFrame(ASCIIToUTF16("//iframe[@id='login']"),
    192                                    WideToUTF16Hack(javascript));
    193 }
    194 
    195 void BrowserSigninHtml::HandleSubmitAuth(const ListValue* args) {
    196   std::string json;
    197   if (!args->GetString(0, &json))
    198     NOTREACHED() << "Could not read JSON argument";
    199 
    200   scoped_ptr<DictionaryValue> result(static_cast<DictionaryValue*>(
    201       base::JSONReader::Read(json, false)));
    202   std::string username;
    203   std::string password;
    204   std::string captcha;
    205   std::string access_code;
    206   if (!result.get() ||
    207       !result->GetString("user", &username) ||
    208       !result->GetString("pass", &password) ||
    209       !result->GetString("captcha", &captcha) ||
    210       !result->GetString("access_code", &access_code)) {
    211     LOG(ERROR) << "Unintelligble format for authentication data from page.";
    212     signin_->Cancel();
    213   }
    214   signin_->GetProfileSyncService()->OnUserSubmittedAuth(
    215       username, password, captcha, access_code);
    216 }
    217 
    218 BrowserSignin::BrowserSignin(Profile* profile)
    219     : profile_(profile),
    220       delegate_(NULL),
    221       html_dialog_ui_delegate_(NULL) {
    222   // profile is NULL during testing.
    223   if (profile) {
    224     BrowserSigninResourcesSource* source = new BrowserSigninResourcesSource();
    225     profile->GetChromeURLDataManager()->AddDataSource(source);
    226   }
    227 }
    228 
    229 BrowserSignin::~BrowserSignin() {
    230   delegate_ = NULL;
    231 }
    232 
    233 void BrowserSignin::RequestSignin(TabContents* tab_contents,
    234                                   const string16& suggested_email,
    235                                   const string16& login_message,
    236                                   SigninDelegate* delegate) {
    237   CHECK(tab_contents);
    238   CHECK(delegate);
    239   // Cancel existing request.
    240   if (delegate_)
    241     Cancel();
    242   delegate_ = delegate;
    243   suggested_email_ = suggested_email;
    244   login_message_ = login_message;
    245   RegisterAuthNotifications();
    246   ShowSigninTabModal(tab_contents);
    247 }
    248 
    249 std::string BrowserSignin::GetSignedInUsername() const {
    250   std::string username =
    251       profile_->GetPrefs()->GetString(prefs::kGoogleServicesUsername);
    252   VLOG(1) << "GetSignedInUsername: " << username;
    253   return username;
    254 }
    255 
    256 void BrowserSignin::Observe(NotificationType type,
    257                             const NotificationSource& source,
    258                             const NotificationDetails& details) {
    259   switch (type.value) {
    260     case NotificationType::GOOGLE_SIGNIN_SUCCESSFUL: {
    261       VLOG(1) << "GOOGLE_SIGNIN_SUCCESSFUL";
    262       if (delegate_)
    263         delegate_->OnLoginSuccess();
    264       // Close the dialog.
    265       OnLoginFinished();
    266       break;
    267     }
    268     case NotificationType::GOOGLE_SIGNIN_FAILED: {
    269       VLOG(1) << "GOOGLE_SIGNIN_FAILED";
    270       // The signin failed, refresh the UI with error information.
    271       html_dialog_ui_delegate_->ReloadUI();
    272       break;
    273     }
    274     default:
    275       NOTREACHED();
    276   }
    277 }
    278 
    279 void BrowserSignin::Cancel() {
    280   if (delegate_) {
    281     delegate_->OnLoginFailure(GoogleServiceAuthError(
    282         GoogleServiceAuthError::REQUEST_CANCELED));
    283     GetProfileSyncService()->OnUserCancelledDialog();
    284   }
    285   OnLoginFinished();
    286 }
    287 
    288 void BrowserSignin::OnLoginFinished() {
    289   if (html_dialog_ui_delegate_)
    290     html_dialog_ui_delegate_->ForceDialogClose();
    291   // The dialog will be deleted by WebUI due to the dialog close,
    292   // don't hold a reference.
    293   html_dialog_ui_delegate_ = NULL;
    294 
    295   if (delegate_) {
    296     UnregisterAuthNotifications();
    297     delegate_ = NULL;
    298   }
    299 }
    300 
    301 ProfileSyncService* BrowserSignin::GetProfileSyncService() const {
    302   return profile_->GetProfileSyncService();
    303 }
    304 
    305 BrowserSigninHtml* BrowserSignin::CreateHtmlDialogUI() {
    306   return new BrowserSigninHtml(this, suggested_email_, login_message_);
    307 }
    308 
    309 void BrowserSignin::RegisterAuthNotifications() {
    310   registrar_.Add(this,
    311                  NotificationType::GOOGLE_SIGNIN_SUCCESSFUL,
    312                  Source<Profile>(profile_));
    313   registrar_.Add(this,
    314                  NotificationType::GOOGLE_SIGNIN_FAILED,
    315                  Source<Profile>(profile_));
    316 }
    317 
    318 void BrowserSignin::UnregisterAuthNotifications() {
    319   registrar_.Remove(this,
    320                     NotificationType::GOOGLE_SIGNIN_SUCCESSFUL,
    321                     Source<Profile>(profile_));
    322   registrar_.Remove(this,
    323                     NotificationType::GOOGLE_SIGNIN_FAILED,
    324                     Source<Profile>(profile_));
    325 }
    326 
    327 void BrowserSignin::ShowSigninTabModal(TabContents* tab_contents) {
    328 //  TODO(johnnyg): Need a linux views implementation for ConstrainedHtmlDialog.
    329 #if defined(OS_WIN) || defined(OS_CHROMEOS) || !defined(TOOLKIT_VIEWS)
    330   html_dialog_ui_delegate_ = CreateHtmlDialogUI();
    331   ConstrainedHtmlUI::CreateConstrainedHtmlDialog(profile_,
    332                                                  html_dialog_ui_delegate_,
    333                                                  tab_contents);
    334 #endif
    335 }
    336