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 "chrome/grit/generated_resources.h" 13#include "content/public/browser/browser_thread.h" 14#include "content/public/browser/web_contents.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 35class 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 51CertificateSelectorTableModel::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 63int CertificateSelectorTableModel::RowCount() { 64 return items_.size(); 65} 66 67base::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 76void CertificateSelectorTableModel::SetObserver( 77 ui::TableModelObserver* observer) { 78} 79 80/////////////////////////////////////////////////////////////////////////////// 81// SSLClientCertificateSelector: 82 83SSLClientCertificateSelector::SSLClientCertificateSelector( 84 content::WebContents* web_contents, 85 const net::HttpNetworkSession* network_session, 86 const scoped_refptr<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.get())), 90 web_contents_(web_contents), 91 table_(NULL), 92 view_cert_button_(NULL) { 93 DVLOG(1) << __FUNCTION__; 94} 95 96SSLClientCertificateSelector::~SSLClientCertificateSelector() { 97 table_->SetModel(NULL); 98} 99 100void 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 143net::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 154void SSLClientCertificateSelector::OnCertSelectedByNotification() { 155 DVLOG(1) << __FUNCTION__; 156 GetWidget()->Close(); 157} 158 159/////////////////////////////////////////////////////////////////////////////// 160// DialogDelegateView implementation: 161 162bool SSLClientCertificateSelector::CanResize() const { 163 return true; 164} 165 166base::string16 SSLClientCertificateSelector::GetWindowTitle() const { 167 return l10n_util::GetStringUTF16(IDS_CLIENT_CERT_DIALOG_TITLE); 168} 169 170void SSLClientCertificateSelector::DeleteDelegate() { 171 DVLOG(1) << __FUNCTION__; 172 delete this; 173} 174 175bool SSLClientCertificateSelector::IsDialogButtonEnabled( 176 ui::DialogButton button) const { 177 if (button == ui::DIALOG_BUTTON_OK) 178 return !!GetSelectedCert(); 179 return true; 180} 181 182bool SSLClientCertificateSelector::Cancel() { 183 DVLOG(1) << __FUNCTION__; 184 StopObserving(); 185 CertificateSelected(NULL); 186 return true; 187} 188 189bool SSLClientCertificateSelector::Accept() { 190 DVLOG(1) << __FUNCTION__; 191 scoped_refptr<net::X509Certificate> cert = GetSelectedCert(); 192 if (cert.get()) { 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.get(), 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.get()); 208#endif 209 return false; // Unlocked() will close the dialog. 210 } 211 212 return false; 213} 214 215views::View* SSLClientCertificateSelector::GetInitiallyFocusedView() { 216 return table_; 217} 218 219views::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 227ui::ModalType SSLClientCertificateSelector::GetModalType() const { 228 return ui::MODAL_TYPE_CHILD; 229} 230 231/////////////////////////////////////////////////////////////////////////////// 232// views::ButtonListener implementation: 233 234void 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: 247void SSLClientCertificateSelector::OnSelectionChanged() { 248 GetDialogClientView()->ok_button()->SetEnabled(!!GetSelectedCert()); 249} 250 251void SSLClientCertificateSelector::OnDoubleClick() { 252 if (Accept()) 253 GetWidget()->Close(); 254} 255 256/////////////////////////////////////////////////////////////////////////////// 257// SSLClientCertificateSelector private methods: 258 259void 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 267void SSLClientCertificateSelector::Unlocked(net::X509Certificate* cert) { 268 DVLOG(1) << __FUNCTION__; 269 CertificateSelected(cert); 270 GetWidget()->Close(); 271} 272 273namespace chrome { 274 275void 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