Remove ConstrainedWindowGtk::CloseWebContentsModalDialog()
[chromium-blink-merge.git] / chrome / browser / ui / gtk / ssl_client_certificate_selector.cc
blob39afc027fcc4256a262de17cd334953b69999799
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.
5 #include "chrome/browser/ssl/ssl_client_certificate_selector.h"
7 #include <gtk/gtk.h>
9 #include <string>
10 #include <vector>
12 #include "base/bind.h"
13 #include "base/i18n/time_formatting.h"
14 #include "base/logging.h"
15 #include "base/utf_string_conversions.h"
16 #include "chrome/browser/certificate_viewer.h"
17 #include "chrome/browser/ssl/ssl_client_auth_observer.h"
18 #include "chrome/browser/ui/crypto_module_password_dialog.h"
19 #include "chrome/browser/ui/gtk/constrained_window_gtk.h"
20 #include "chrome/browser/ui/gtk/gtk_util.h"
21 #include "chrome/common/net/x509_certificate_model.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "grit/generated_resources.h"
24 #include "net/base/ssl_cert_request_info.h"
25 #include "net/base/x509_certificate.h"
26 #include "ui/base/gtk/gtk_compat.h"
27 #include "ui/base/gtk/gtk_hig_constants.h"
28 #include "ui/base/gtk/gtk_signal.h"
29 #include "ui/base/gtk/owned_widget_gtk.h"
30 #include "ui/base/l10n/l10n_util.h"
31 #include "ui/gfx/native_widget_types.h"
33 using content::BrowserThread;
34 using content::WebContents;
36 namespace {
38 enum {
39 RESPONSE_SHOW_CERT_INFO = 1,
42 ///////////////////////////////////////////////////////////////////////////////
43 // SSLClientCertificateSelector
45 class SSLClientCertificateSelector : public SSLClientAuthObserver,
46 public ConstrainedWindowGtkDelegate {
47 public:
48 explicit SSLClientCertificateSelector(
49 WebContents* parent,
50 const net::HttpNetworkSession* network_session,
51 net::SSLCertRequestInfo* cert_request_info,
52 const base::Callback<void(net::X509Certificate*)>& callback);
53 virtual ~SSLClientCertificateSelector();
55 void Show();
57 // SSLClientAuthObserver implementation:
58 virtual void OnCertSelectedByNotification() OVERRIDE;
60 // ConstrainedWindowGtkDelegate implementation:
61 virtual GtkWidget* GetWidgetRoot() OVERRIDE { return root_widget_.get(); }
62 virtual GtkWidget* GetFocusWidget() OVERRIDE;
63 virtual void DeleteDelegate() OVERRIDE;
65 private:
66 void PopulateCerts();
68 net::X509Certificate* GetSelectedCert();
70 static std::string FormatComboBoxText(
71 net::X509Certificate::OSCertHandle cert,
72 const std::string& nickname);
73 static std::string FormatDetailsText(
74 net::X509Certificate::OSCertHandle cert);
76 // Callback after unlocking certificate slot.
77 void Unlocked();
79 CHROMEGTK_CALLBACK_0(SSLClientCertificateSelector, void, OnComboBoxChanged);
80 CHROMEGTK_CALLBACK_0(SSLClientCertificateSelector, void, OnViewClicked);
81 CHROMEGTK_CALLBACK_0(SSLClientCertificateSelector, void, OnCancelClicked);
82 CHROMEGTK_CALLBACK_0(SSLClientCertificateSelector, void, OnOkClicked);
83 CHROMEGTK_CALLBACK_1(SSLClientCertificateSelector, void, OnPromptShown,
84 GtkWidget*);
86 std::vector<std::string> details_strings_;
88 GtkWidget* cert_combo_box_;
89 GtkTextBuffer* cert_details_buffer_;
91 ui::OwnedWidgetGtk root_widget_;
92 // Hold on to the select button to focus it.
93 GtkWidget* select_button_;
95 WebContents* web_contents_;
96 ConstrainedWindowGtk* window_;
98 DISALLOW_COPY_AND_ASSIGN(SSLClientCertificateSelector);
101 SSLClientCertificateSelector::SSLClientCertificateSelector(
102 WebContents* web_contents,
103 const net::HttpNetworkSession* network_session,
104 net::SSLCertRequestInfo* cert_request_info,
105 const base::Callback<void(net::X509Certificate*)>& callback)
106 : SSLClientAuthObserver(network_session, cert_request_info, callback),
107 web_contents_(web_contents),
108 window_(NULL) {
109 root_widget_.Own(gtk_vbox_new(FALSE, ui::kControlSpacing));
111 GtkWidget* site_vbox = gtk_vbox_new(FALSE, ui::kControlSpacing);
112 gtk_box_pack_start(GTK_BOX(root_widget_.get()), site_vbox,
113 FALSE, FALSE, 0);
115 GtkWidget* site_description_label = gtk_util::CreateBoldLabel(
116 l10n_util::GetStringUTF8(IDS_CERT_SELECTOR_SITE_DESCRIPTION_LABEL));
117 gtk_box_pack_start(GTK_BOX(site_vbox), site_description_label,
118 FALSE, FALSE, 0);
120 GtkWidget* site_label = gtk_label_new(
121 cert_request_info->host_and_port.c_str());
122 gtk_util::LeftAlignMisc(site_label);
123 gtk_box_pack_start(GTK_BOX(site_vbox), site_label, FALSE, FALSE, 0);
125 GtkWidget* selector_vbox = gtk_vbox_new(FALSE, ui::kControlSpacing);
126 gtk_box_pack_start(GTK_BOX(root_widget_.get()), selector_vbox,
127 TRUE, TRUE, 0);
129 GtkWidget* choose_description_label = gtk_util::CreateBoldLabel(
130 l10n_util::GetStringUTF8(IDS_CERT_SELECTOR_CHOOSE_DESCRIPTION_LABEL));
131 gtk_box_pack_start(GTK_BOX(selector_vbox), choose_description_label,
132 FALSE, FALSE, 0);
135 cert_combo_box_ = gtk_combo_box_new_text();
136 g_signal_connect(cert_combo_box_, "changed",
137 G_CALLBACK(OnComboBoxChangedThunk), this);
138 gtk_box_pack_start(GTK_BOX(selector_vbox), cert_combo_box_,
139 FALSE, FALSE, 0);
141 GtkWidget* details_label = gtk_label_new(l10n_util::GetStringUTF8(
142 IDS_CERT_SELECTOR_DETAILS_DESCRIPTION_LABEL).c_str());
143 gtk_util::LeftAlignMisc(details_label);
144 gtk_box_pack_start(GTK_BOX(selector_vbox), details_label, FALSE, FALSE, 0);
146 // TODO(mattm): fix text view coloring (should have grey background).
147 GtkWidget* cert_details_view = gtk_text_view_new();
148 gtk_text_view_set_editable(GTK_TEXT_VIEW(cert_details_view), FALSE);
149 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(cert_details_view), GTK_WRAP_WORD);
150 cert_details_buffer_ = gtk_text_view_get_buffer(
151 GTK_TEXT_VIEW(cert_details_view));
152 // We put the details in a frame instead of a scrolled window so that the
153 // entirety will be visible without requiring scrolling or expanding the
154 // dialog. This does however mean the dialog will grow itself if you switch
155 // to different cert that has longer details text.
156 GtkWidget* details_frame = gtk_frame_new(NULL);
157 gtk_frame_set_shadow_type(GTK_FRAME(details_frame), GTK_SHADOW_ETCHED_IN);
158 gtk_container_add(GTK_CONTAINER(details_frame), cert_details_view);
159 gtk_box_pack_start(GTK_BOX(selector_vbox), details_frame, TRUE, TRUE, 0);
161 // And then create a set of buttons like a GtkDialog would.
162 GtkWidget* button_box = gtk_hbutton_box_new();
163 gtk_button_box_set_layout(GTK_BUTTON_BOX(button_box), GTK_BUTTONBOX_END);
164 gtk_box_set_spacing(GTK_BOX(button_box), ui::kControlSpacing);
165 gtk_box_pack_end(GTK_BOX(root_widget_.get()), button_box, FALSE, FALSE, 0);
167 GtkWidget* view_button = gtk_button_new_with_mnemonic(
168 l10n_util::GetStringUTF8(IDS_PAGEINFO_CERT_INFO_BUTTON).c_str());
169 gtk_box_pack_start(GTK_BOX(button_box), view_button, FALSE, FALSE, 0);
170 g_signal_connect(view_button, "clicked",
171 G_CALLBACK(OnViewClickedThunk), this);
173 GtkWidget* cancel_button = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
174 gtk_box_pack_end(GTK_BOX(button_box), cancel_button, FALSE, FALSE, 0);
175 g_signal_connect(cancel_button, "clicked",
176 G_CALLBACK(OnCancelClickedThunk), this);
178 GtkWidget* select_button = gtk_button_new_from_stock(GTK_STOCK_OK);
179 gtk_box_pack_end(GTK_BOX(button_box), select_button, FALSE, FALSE, 0);
180 g_signal_connect(select_button, "clicked",
181 G_CALLBACK(OnOkClickedThunk), this);
183 // When we are attached to a window, focus the select button.
184 select_button_ = select_button;
185 g_signal_connect(root_widget_.get(), "hierarchy-changed",
186 G_CALLBACK(OnPromptShownThunk), this);
187 PopulateCerts();
189 gtk_widget_show_all(root_widget_.get());
191 StartObserving();
194 SSLClientCertificateSelector::~SSLClientCertificateSelector() {
195 root_widget_.Destroy();
198 void SSLClientCertificateSelector::Show() {
199 DCHECK(!window_);
200 window_ = new ConstrainedWindowGtk(web_contents_, this);
203 void SSLClientCertificateSelector::OnCertSelectedByNotification() {
204 DCHECK(window_);
205 gtk_widget_destroy(window_->widget());
208 GtkWidget* SSLClientCertificateSelector::GetFocusWidget() {
209 return select_button_;
212 void SSLClientCertificateSelector::DeleteDelegate() {
213 // The dialog was closed by escape key.
214 StopObserving();
215 CertificateSelected(NULL);
216 delete this;
219 void SSLClientCertificateSelector::PopulateCerts() {
220 std::vector<std::string> nicknames;
221 x509_certificate_model::GetNicknameStringsFromCertList(
222 cert_request_info()->client_certs,
223 l10n_util::GetStringUTF8(IDS_CERT_SELECTOR_CERT_EXPIRED),
224 l10n_util::GetStringUTF8(IDS_CERT_SELECTOR_CERT_NOT_YET_VALID),
225 &nicknames);
227 DCHECK_EQ(nicknames.size(),
228 cert_request_info()->client_certs.size());
230 for (size_t i = 0; i < cert_request_info()->client_certs.size(); ++i) {
231 net::X509Certificate::OSCertHandle cert =
232 cert_request_info()->client_certs[i]->os_cert_handle();
234 details_strings_.push_back(FormatDetailsText(cert));
236 gtk_combo_box_append_text(
237 GTK_COMBO_BOX(cert_combo_box_),
238 FormatComboBoxText(cert, nicknames[i]).c_str());
241 // Auto-select the first cert.
242 gtk_combo_box_set_active(GTK_COMBO_BOX(cert_combo_box_), 0);
245 net::X509Certificate* SSLClientCertificateSelector::GetSelectedCert() {
246 int selected = gtk_combo_box_get_active(GTK_COMBO_BOX(cert_combo_box_));
247 if (selected >= 0 &&
248 selected < static_cast<int>(
249 cert_request_info()->client_certs.size()))
250 return cert_request_info()->client_certs[selected];
251 return NULL;
254 // static
255 std::string SSLClientCertificateSelector::FormatComboBoxText(
256 net::X509Certificate::OSCertHandle cert, const std::string& nickname) {
257 std::string rv(nickname);
258 rv += " [";
259 rv += x509_certificate_model::GetSerialNumberHexified(cert, "");
260 rv += ']';
261 return rv;
264 // static
265 std::string SSLClientCertificateSelector::FormatDetailsText(
266 net::X509Certificate::OSCertHandle cert) {
267 std::string rv;
269 rv += l10n_util::GetStringFUTF8(
270 IDS_CERT_SUBJECTNAME_FORMAT,
271 UTF8ToUTF16(x509_certificate_model::GetSubjectName(cert)));
273 rv += "\n ";
274 rv += l10n_util::GetStringFUTF8(
275 IDS_CERT_SERIAL_NUMBER_FORMAT,
276 UTF8ToUTF16(
277 x509_certificate_model::GetSerialNumberHexified(cert, "")));
279 base::Time issued, expires;
280 if (x509_certificate_model::GetTimes(cert, &issued, &expires)) {
281 string16 issued_str = base::TimeFormatShortDateAndTime(issued);
282 string16 expires_str = base::TimeFormatShortDateAndTime(expires);
283 rv += "\n ";
284 rv += l10n_util::GetStringFUTF8(IDS_CERT_VALIDITY_RANGE_FORMAT,
285 issued_str, expires_str);
288 std::vector<std::string> usages;
289 x509_certificate_model::GetUsageStrings(cert, &usages);
290 if (usages.size()) {
291 rv += "\n ";
292 rv += l10n_util::GetStringFUTF8(IDS_CERT_X509_EXTENDED_KEY_USAGE_FORMAT,
293 UTF8ToUTF16(JoinString(usages, ',')));
296 std::string key_usage_str = x509_certificate_model::GetKeyUsageString(cert);
297 if (!key_usage_str.empty()) {
298 rv += "\n ";
299 rv += l10n_util::GetStringFUTF8(IDS_CERT_X509_KEY_USAGE_FORMAT,
300 UTF8ToUTF16(key_usage_str));
303 std::vector<std::string> email_addresses;
304 x509_certificate_model::GetEmailAddresses(cert, &email_addresses);
305 if (email_addresses.size()) {
306 rv += "\n ";
307 rv += l10n_util::GetStringFUTF8(
308 IDS_CERT_EMAIL_ADDRESSES_FORMAT,
309 UTF8ToUTF16(JoinString(email_addresses, ',')));
312 rv += '\n';
313 rv += l10n_util::GetStringFUTF8(
314 IDS_CERT_ISSUERNAME_FORMAT,
315 UTF8ToUTF16(x509_certificate_model::GetIssuerName(cert)));
317 string16 token(UTF8ToUTF16(x509_certificate_model::GetTokenName(cert)));
318 if (!token.empty()) {
319 rv += '\n';
320 rv += l10n_util::GetStringFUTF8(IDS_CERT_TOKEN_FORMAT, token);
323 return rv;
326 void SSLClientCertificateSelector::Unlocked() {
327 // TODO(mattm): refactor so we don't need to call GetSelectedCert again.
328 net::X509Certificate* cert = GetSelectedCert();
329 CertificateSelected(cert);
330 DCHECK(window_);
331 gtk_widget_destroy(window_->widget());
334 void SSLClientCertificateSelector::OnComboBoxChanged(GtkWidget* combo_box) {
335 int selected = gtk_combo_box_get_active(
336 GTK_COMBO_BOX(cert_combo_box_));
337 if (selected < 0)
338 return;
339 gtk_text_buffer_set_text(cert_details_buffer_,
340 details_strings_[selected].c_str(),
341 details_strings_[selected].size());
344 void SSLClientCertificateSelector::OnViewClicked(GtkWidget* button) {
345 net::X509Certificate* cert = GetSelectedCert();
346 if (cert) {
347 GtkWidget* toplevel = gtk_widget_get_toplevel(root_widget_.get());
348 ShowCertificateViewer(web_contents_, GTK_WINDOW(toplevel), cert);
352 void SSLClientCertificateSelector::OnCancelClicked(GtkWidget* button) {
353 CertificateSelected(NULL);
354 DCHECK(window_);
355 gtk_widget_destroy(window_->widget());
358 void SSLClientCertificateSelector::OnOkClicked(GtkWidget* button) {
359 net::X509Certificate* cert = GetSelectedCert();
361 // Remove the observer before we try unlocking, otherwise we might act on a
362 // notification while waiting for the unlock dialog, causing us to delete
363 // ourself before the Unlocked callback gets called.
364 StopObserving();
366 chrome::UnlockCertSlotIfNecessary(
367 cert,
368 chrome::kCryptoModulePasswordClientAuth,
369 cert_request_info()->host_and_port,
370 base::Bind(&SSLClientCertificateSelector::Unlocked,
371 base::Unretained(this)));
374 void SSLClientCertificateSelector::OnPromptShown(GtkWidget* widget,
375 GtkWidget* previous_toplevel) {
376 if (!root_widget_.get() ||
377 !gtk_widget_is_toplevel(gtk_widget_get_toplevel(root_widget_.get())))
378 return;
379 gtk_widget_set_can_default(select_button_, TRUE);
380 gtk_widget_grab_default(select_button_);
383 } // namespace
385 namespace chrome {
387 void ShowSSLClientCertificateSelector(
388 content::WebContents* contents,
389 const net::HttpNetworkSession* network_session,
390 net::SSLCertRequestInfo* cert_request_info,
391 const base::Callback<void(net::X509Certificate*)>& callback) {
392 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
393 (new SSLClientCertificateSelector(
394 contents, network_session, cert_request_info, callback))->Show();
397 } // namespace chrome