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