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