ssl_client_certificate_selector.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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