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