Home | History | Annotate | Download | only in views
      1 // Copyright (c) 2012 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/views/ssl_client_certificate_selector.h"
      6 
      7 #include "base/compiler_specific.h"
      8 #include "base/logging.h"
      9 #include "base/strings/utf_string_conversions.h"
     10 #include "chrome/browser/certificate_viewer.h"
     11 #include "chrome/browser/ui/views/constrained_window_views.h"
     12 #include "content/public/browser/browser_thread.h"
     13 #include "content/public/browser/web_contents.h"
     14 #include "grit/generated_resources.h"
     15 #include "net/cert/x509_certificate.h"
     16 #include "net/ssl/ssl_cert_request_info.h"
     17 #include "ui/base/l10n/l10n_util.h"
     18 #include "ui/base/models/table_model.h"
     19 #include "ui/base/models/table_model_observer.h"
     20 #include "ui/views/controls/button/label_button.h"
     21 #include "ui/views/controls/label.h"
     22 #include "ui/views/controls/table/table_view.h"
     23 #include "ui/views/layout/grid_layout.h"
     24 #include "ui/views/layout/layout_constants.h"
     25 #include "ui/views/widget/widget.h"
     26 #include "ui/views/window/dialog_client_view.h"
     27 
     28 #if defined(USE_NSS)
     29 #include "chrome/browser/ui/crypto_module_password_dialog_nss.h"
     30 #endif
     31 
     32 ///////////////////////////////////////////////////////////////////////////////
     33 // CertificateSelectorTableModel:
     34 
     35 class CertificateSelectorTableModel : public ui::TableModel {
     36  public:
     37   explicit CertificateSelectorTableModel(
     38       net::SSLCertRequestInfo* cert_request_info);
     39 
     40   // ui::TableModel implementation:
     41   virtual int RowCount() OVERRIDE;
     42   virtual base::string16 GetText(int index, int column_id) OVERRIDE;
     43   virtual void SetObserver(ui::TableModelObserver* observer) OVERRIDE;
     44 
     45  private:
     46   std::vector<base::string16> items_;
     47 
     48   DISALLOW_COPY_AND_ASSIGN(CertificateSelectorTableModel);
     49 };
     50 
     51 CertificateSelectorTableModel::CertificateSelectorTableModel(
     52     net::SSLCertRequestInfo* cert_request_info) {
     53   for (size_t i = 0; i < cert_request_info->client_certs.size(); ++i) {
     54     net::X509Certificate* cert = cert_request_info->client_certs[i].get();
     55     base::string16 text = l10n_util::GetStringFUTF16(
     56         IDS_CERT_SELECTOR_TABLE_CERT_FORMAT,
     57         base::UTF8ToUTF16(cert->subject().GetDisplayName()),
     58         base::UTF8ToUTF16(cert->issuer().GetDisplayName()));
     59     items_.push_back(text);
     60   }
     61 }
     62 
     63 int CertificateSelectorTableModel::RowCount() {
     64   return items_.size();
     65 }
     66 
     67 base::string16 CertificateSelectorTableModel::GetText(int index,
     68                                                       int column_id) {
     69   DCHECK_EQ(column_id, 0);
     70   DCHECK_GE(index, 0);
     71   DCHECK_LT(index, static_cast<int>(items_.size()));
     72 
     73   return items_[index];
     74 }
     75 
     76 void CertificateSelectorTableModel::SetObserver(
     77     ui::TableModelObserver* observer) {
     78 }
     79 
     80 ///////////////////////////////////////////////////////////////////////////////
     81 // SSLClientCertificateSelector:
     82 
     83 SSLClientCertificateSelector::SSLClientCertificateSelector(
     84     content::WebContents* web_contents,
     85     const net::HttpNetworkSession* network_session,
     86     net::SSLCertRequestInfo* cert_request_info,
     87     const chrome::SelectCertificateCallback& callback)
     88     : SSLClientAuthObserver(network_session, cert_request_info, callback),
     89       model_(new CertificateSelectorTableModel(cert_request_info)),
     90       web_contents_(web_contents),
     91       table_(NULL),
     92       view_cert_button_(NULL) {
     93   DVLOG(1) << __FUNCTION__;
     94 }
     95 
     96 SSLClientCertificateSelector::~SSLClientCertificateSelector() {
     97   table_->SetModel(NULL);
     98 }
     99 
    100 void SSLClientCertificateSelector::Init() {
    101   views::GridLayout* layout = views::GridLayout::CreatePanel(this);
    102   SetLayoutManager(layout);
    103 
    104   const int column_set_id = 0;
    105   views::ColumnSet* column_set = layout->AddColumnSet(column_set_id);
    106   column_set->AddColumn(
    107       views::GridLayout::FILL, views::GridLayout::FILL,
    108       1, views::GridLayout::USE_PREF, 0, 0);
    109 
    110   layout->StartRow(0, column_set_id);
    111   base::string16 text = l10n_util::GetStringFUTF16(
    112       IDS_CLIENT_CERT_DIALOG_TEXT,
    113       base::ASCIIToUTF16(cert_request_info()->host_and_port.ToString()));
    114   views::Label* label = new views::Label(text);
    115   label->SetMultiLine(true);
    116   label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
    117   label->SetAllowCharacterBreak(true);
    118   layout->AddView(label);
    119 
    120   layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
    121 
    122   // The dimensions of the certificate selector table view, in pixels.
    123   static const int kTableViewWidth = 400;
    124   static const int kTableViewHeight = 100;
    125 
    126   CreateCertTable();
    127   layout->StartRow(1, column_set_id);
    128   layout->AddView(table_->CreateParentIfNecessary(), 1, 1,
    129                   views::GridLayout::FILL,
    130                   views::GridLayout::FILL, kTableViewWidth, kTableViewHeight);
    131 
    132   layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
    133 
    134   StartObserving();
    135 
    136   ShowWebModalDialogViews(this, web_contents_);
    137 
    138   // Select the first row automatically.  This must be done after the dialog has
    139   // been created.
    140   table_->Select(0);
    141 }
    142 
    143 net::X509Certificate* SSLClientCertificateSelector::GetSelectedCert() const {
    144   int selected = table_->FirstSelectedRow();
    145   if (selected >= 0 &&
    146       selected < static_cast<int>(cert_request_info()->client_certs.size()))
    147     return cert_request_info()->client_certs[selected].get();
    148   return NULL;
    149 }
    150 
    151 ///////////////////////////////////////////////////////////////////////////////
    152 // SSLClientAuthObserver implementation:
    153 
    154 void SSLClientCertificateSelector::OnCertSelectedByNotification() {
    155   DVLOG(1) << __FUNCTION__;
    156   GetWidget()->Close();
    157 }
    158 
    159 ///////////////////////////////////////////////////////////////////////////////
    160 // DialogDelegateView implementation:
    161 
    162 bool SSLClientCertificateSelector::CanResize() const {
    163   return true;
    164 }
    165 
    166 base::string16 SSLClientCertificateSelector::GetWindowTitle() const {
    167   return l10n_util::GetStringUTF16(IDS_CLIENT_CERT_DIALOG_TITLE);
    168 }
    169 
    170 void SSLClientCertificateSelector::DeleteDelegate() {
    171   DVLOG(1) << __FUNCTION__;
    172   delete this;
    173 }
    174 
    175 bool SSLClientCertificateSelector::IsDialogButtonEnabled(
    176     ui::DialogButton button) const {
    177   if (button == ui::DIALOG_BUTTON_OK)
    178     return !!GetSelectedCert();
    179   return true;
    180 }
    181 
    182 bool SSLClientCertificateSelector::Cancel() {
    183   DVLOG(1) << __FUNCTION__;
    184   StopObserving();
    185   CertificateSelected(NULL);
    186   return true;
    187 }
    188 
    189 bool SSLClientCertificateSelector::Accept() {
    190   DVLOG(1) << __FUNCTION__;
    191   scoped_refptr<net::X509Certificate> cert = GetSelectedCert();
    192   if (cert) {
    193     // Remove the observer before we try unlocking, otherwise we might act on a
    194     // notification while waiting for the unlock dialog, causing us to delete
    195     // ourself before the Unlocked callback gets called.
    196     StopObserving();
    197 #if defined(USE_NSS)
    198     chrome::UnlockCertSlotIfNecessary(
    199         cert,
    200         chrome::kCryptoModulePasswordClientAuth,
    201         cert_request_info()->host_and_port,
    202         GetWidget()->GetNativeView(),
    203         base::Bind(&SSLClientCertificateSelector::Unlocked,
    204                    base::Unretained(this),
    205                    cert));
    206 #else
    207     Unlocked(cert);
    208 #endif
    209     return false;  // Unlocked() will close the dialog.
    210   }
    211 
    212   return false;
    213 }
    214 
    215 views::View* SSLClientCertificateSelector::GetInitiallyFocusedView() {
    216   return table_;
    217 }
    218 
    219 views::View* SSLClientCertificateSelector::CreateExtraView() {
    220   DCHECK(!view_cert_button_);
    221   view_cert_button_ = new views::LabelButton(this,
    222       l10n_util::GetStringUTF16(IDS_PAGEINFO_CERT_INFO_BUTTON));
    223   view_cert_button_->SetStyle(views::Button::STYLE_BUTTON);
    224   return view_cert_button_;
    225 }
    226 
    227 ui::ModalType SSLClientCertificateSelector::GetModalType() const {
    228   return ui::MODAL_TYPE_CHILD;
    229 }
    230 
    231 ///////////////////////////////////////////////////////////////////////////////
    232 // views::ButtonListener implementation:
    233 
    234 void SSLClientCertificateSelector::ButtonPressed(
    235     views::Button* sender, const ui::Event& event) {
    236   if (sender == view_cert_button_) {
    237     net::X509Certificate* cert = GetSelectedCert();
    238     if (cert)
    239       ShowCertificateViewer(web_contents_,
    240                             web_contents_->GetTopLevelNativeWindow(),
    241                             cert);
    242   }
    243 }
    244 
    245 ///////////////////////////////////////////////////////////////////////////////
    246 // views::TableViewObserver implementation:
    247 void SSLClientCertificateSelector::OnSelectionChanged() {
    248   GetDialogClientView()->ok_button()->SetEnabled(!!GetSelectedCert());
    249 }
    250 
    251 void SSLClientCertificateSelector::OnDoubleClick() {
    252   if (Accept())
    253     GetWidget()->Close();
    254 }
    255 
    256 ///////////////////////////////////////////////////////////////////////////////
    257 // SSLClientCertificateSelector private methods:
    258 
    259 void SSLClientCertificateSelector::CreateCertTable() {
    260   std::vector<ui::TableColumn> columns;
    261   columns.push_back(ui::TableColumn());
    262   table_ = new views::TableView(model_.get(), columns, views::TEXT_ONLY,
    263                                 true /* single_selection */);
    264   table_->SetObserver(this);
    265 }
    266 
    267 void SSLClientCertificateSelector::Unlocked(net::X509Certificate* cert) {
    268   DVLOG(1) << __FUNCTION__;
    269   CertificateSelected(cert);
    270   GetWidget()->Close();
    271 }
    272 
    273 namespace chrome {
    274 
    275 void ShowSSLClientCertificateSelector(
    276     content::WebContents* contents,
    277     const net::HttpNetworkSession* network_session,
    278     net::SSLCertRequestInfo* cert_request_info,
    279     const chrome::SelectCertificateCallback& callback) {
    280   DVLOG(1) << __FUNCTION__ << " " << contents;
    281   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
    282   (new SSLClientCertificateSelector(
    283        contents, network_session, cert_request_info, callback))->Init();
    284 }
    285 
    286 }  // namespace chrome
    287