Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(614)

Side by Side Diff: chrome/browser/ui/gtk/ssl_client_certificate_selector.cc

Issue 231733005: Delete the GTK+ port of Chrome. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Remerge to ToT Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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/ssl/ssl_client_certificate_selector.h"
6
7 #include <gtk/gtk.h>
8
9 #include <string>
10 #include <vector>
11
12 #include "base/bind.h"
13 #include "base/i18n/time_formatting.h"
14 #include "base/logging.h"
15 #include "base/strings/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_nss.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 "components/web_modal/web_contents_modal_dialog_manager.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "grit/generated_resources.h"
25 #include "net/cert/x509_certificate.h"
26 #include "net/ssl/ssl_cert_request_info.h"
27 #include "ui/base/gtk/gtk_hig_constants.h"
28 #include "ui/base/gtk/gtk_signal.h"
29 #include "ui/base/l10n/l10n_util.h"
30 #include "ui/gfx/gtk_compat.h"
31 #include "ui/gfx/native_widget_types.h"
32 #include "ui/gfx/scoped_gobject.h"
33
34 using content::BrowserThread;
35 using content::WebContents;
36 using web_modal::WebContentsModalDialogManager;
37
38 namespace {
39
40 enum {
41 RESPONSE_SHOW_CERT_INFO = 1,
42 };
43
44 ///////////////////////////////////////////////////////////////////////////////
45 // SSLClientCertificateSelector
46
47 class SSLClientCertificateSelector : public SSLClientAuthObserver {
48 public:
49 explicit SSLClientCertificateSelector(
50 WebContents* parent,
51 const net::HttpNetworkSession* network_session,
52 net::SSLCertRequestInfo* cert_request_info,
53 const base::Callback<void(net::X509Certificate*)>& callback);
54 virtual ~SSLClientCertificateSelector();
55
56 void Show();
57
58 // SSLClientAuthObserver implementation:
59 virtual void OnCertSelectedByNotification() OVERRIDE;
60
61 private:
62 void PopulateCerts();
63
64 net::X509Certificate* GetSelectedCert();
65
66 static std::string FormatComboBoxText(
67 net::X509Certificate::OSCertHandle cert,
68 const std::string& nickname);
69 static std::string FormatDetailsText(
70 net::X509Certificate::OSCertHandle cert);
71
72 // Callback after unlocking certificate slot.
73 void Unlocked();
74
75 CHROMEGTK_CALLBACK_0(SSLClientCertificateSelector, void, OnComboBoxChanged);
76 CHROMEGTK_CALLBACK_0(SSLClientCertificateSelector, void, OnViewClicked);
77 CHROMEGTK_CALLBACK_0(SSLClientCertificateSelector, void, OnCancelClicked);
78 CHROMEGTK_CALLBACK_0(SSLClientCertificateSelector, void, OnOkClicked);
79 CHROMEGTK_CALLBACK_1(SSLClientCertificateSelector, void, OnPromptShown,
80 GtkWidget*);
81 CHROMEGTK_CALLBACK_0(SSLClientCertificateSelector, void, OnDestroy);
82
83 std::vector<std::string> details_strings_;
84
85 GtkWidget* cert_combo_box_;
86 GtkTextBuffer* cert_details_buffer_;
87
88 ui::ScopedGObject<GtkWidget>::Type root_widget_;
89 // Hold on to the select button to focus it.
90 GtkWidget* select_button_;
91
92 WebContents* web_contents_;
93 GtkWidget* window_;
94
95 DISALLOW_COPY_AND_ASSIGN(SSLClientCertificateSelector);
96 };
97
98 SSLClientCertificateSelector::SSLClientCertificateSelector(
99 WebContents* web_contents,
100 const net::HttpNetworkSession* network_session,
101 net::SSLCertRequestInfo* cert_request_info,
102 const base::Callback<void(net::X509Certificate*)>& callback)
103 : SSLClientAuthObserver(network_session, cert_request_info, callback),
104 web_contents_(web_contents),
105 window_(NULL) {
106 root_widget_.reset(gtk_vbox_new(FALSE, ui::kControlSpacing));
107 g_object_ref_sink(root_widget_.get());
108 g_signal_connect(root_widget_.get(),
109 "destroy",
110 G_CALLBACK(OnDestroyThunk),
111 this);
112
113 GtkWidget* site_vbox = gtk_vbox_new(FALSE, ui::kControlSpacing);
114 gtk_box_pack_start(GTK_BOX(root_widget_.get()), site_vbox,
115 FALSE, FALSE, 0);
116
117 GtkWidget* site_description_label = gtk_util::CreateBoldLabel(
118 l10n_util::GetStringUTF8(IDS_CERT_SELECTOR_SITE_DESCRIPTION_LABEL));
119 gtk_box_pack_start(GTK_BOX(site_vbox), site_description_label,
120 FALSE, FALSE, 0);
121
122 GtkWidget* site_label = gtk_label_new(
123 cert_request_info->host_and_port.ToString().c_str());
124 gtk_util::LeftAlignMisc(site_label);
125 gtk_box_pack_start(GTK_BOX(site_vbox), site_label, FALSE, FALSE, 0);
126
127 GtkWidget* selector_vbox = gtk_vbox_new(FALSE, ui::kControlSpacing);
128 gtk_box_pack_start(GTK_BOX(root_widget_.get()), selector_vbox,
129 TRUE, TRUE, 0);
130
131 GtkWidget* choose_description_label = gtk_util::CreateBoldLabel(
132 l10n_util::GetStringUTF8(IDS_CERT_SELECTOR_CHOOSE_DESCRIPTION_LABEL));
133 gtk_box_pack_start(GTK_BOX(selector_vbox), choose_description_label,
134 FALSE, FALSE, 0);
135
136
137 cert_combo_box_ = gtk_combo_box_new_text();
138 g_signal_connect(cert_combo_box_, "changed",
139 G_CALLBACK(OnComboBoxChangedThunk), this);
140 gtk_box_pack_start(GTK_BOX(selector_vbox), cert_combo_box_,
141 FALSE, FALSE, 0);
142
143 GtkWidget* details_label = gtk_label_new(l10n_util::GetStringUTF8(
144 IDS_CERT_SELECTOR_DETAILS_DESCRIPTION_LABEL).c_str());
145 gtk_util::LeftAlignMisc(details_label);
146 gtk_box_pack_start(GTK_BOX(selector_vbox), details_label, FALSE, FALSE, 0);
147
148 // TODO(mattm): fix text view coloring (should have grey background).
149 GtkWidget* cert_details_view = gtk_text_view_new();
150 gtk_text_view_set_editable(GTK_TEXT_VIEW(cert_details_view), FALSE);
151 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(cert_details_view), GTK_WRAP_WORD);
152 cert_details_buffer_ = gtk_text_view_get_buffer(
153 GTK_TEXT_VIEW(cert_details_view));
154 // We put the details in a frame instead of a scrolled window so that the
155 // entirety will be visible without requiring scrolling or expanding the
156 // dialog. This does however mean the dialog will grow itself if you switch
157 // to different cert that has longer details text.
158 GtkWidget* details_frame = gtk_frame_new(NULL);
159 gtk_frame_set_shadow_type(GTK_FRAME(details_frame), GTK_SHADOW_ETCHED_IN);
160 gtk_container_add(GTK_CONTAINER(details_frame), cert_details_view);
161 gtk_box_pack_start(GTK_BOX(selector_vbox), details_frame, TRUE, TRUE, 0);
162
163 // And then create a set of buttons like a GtkDialog would.
164 GtkWidget* button_box = gtk_hbutton_box_new();
165 gtk_button_box_set_layout(GTK_BUTTON_BOX(button_box), GTK_BUTTONBOX_END);
166 gtk_box_set_spacing(GTK_BOX(button_box), ui::kControlSpacing);
167 gtk_box_pack_end(GTK_BOX(root_widget_.get()), button_box, FALSE, FALSE, 0);
168
169 GtkWidget* view_button = gtk_button_new_with_mnemonic(
170 l10n_util::GetStringUTF8(IDS_PAGEINFO_CERT_INFO_BUTTON).c_str());
171 gtk_box_pack_start(GTK_BOX(button_box), view_button, FALSE, FALSE, 0);
172 g_signal_connect(view_button, "clicked",
173 G_CALLBACK(OnViewClickedThunk), this);
174
175 GtkWidget* cancel_button = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
176 gtk_box_pack_end(GTK_BOX(button_box), cancel_button, FALSE, FALSE, 0);
177 g_signal_connect(cancel_button, "clicked",
178 G_CALLBACK(OnCancelClickedThunk), this);
179
180 GtkWidget* select_button = gtk_button_new_from_stock(GTK_STOCK_OK);
181 gtk_box_pack_end(GTK_BOX(button_box), select_button, FALSE, FALSE, 0);
182 g_signal_connect(select_button, "clicked",
183 G_CALLBACK(OnOkClickedThunk), this);
184
185 // When we are attached to a window, focus the select button.
186 select_button_ = select_button;
187 g_signal_connect(root_widget_.get(), "hierarchy-changed",
188 G_CALLBACK(OnPromptShownThunk), this);
189 PopulateCerts();
190
191 gtk_widget_show_all(root_widget_.get());
192
193 StartObserving();
194 }
195
196 SSLClientCertificateSelector::~SSLClientCertificateSelector() {
197 }
198
199 void SSLClientCertificateSelector::Show() {
200 DCHECK(!window_);
201 window_ = CreateWebContentsModalDialogGtk(root_widget_.get(), select_button_);
202
203 WebContentsModalDialogManager* web_contents_modal_dialog_manager =
204 WebContentsModalDialogManager::FromWebContents(web_contents_);
205 web_contents_modal_dialog_manager->ShowDialog(window_);
206 }
207
208 void SSLClientCertificateSelector::OnCertSelectedByNotification() {
209 DCHECK(window_);
210 gtk_widget_destroy(window_);
211 }
212
213 void SSLClientCertificateSelector::PopulateCerts() {
214 std::vector<std::string> nicknames;
215 x509_certificate_model::GetNicknameStringsFromCertList(
216 cert_request_info()->client_certs,
217 l10n_util::GetStringUTF8(IDS_CERT_SELECTOR_CERT_EXPIRED),
218 l10n_util::GetStringUTF8(IDS_CERT_SELECTOR_CERT_NOT_YET_VALID),
219 &nicknames);
220
221 DCHECK_EQ(nicknames.size(),
222 cert_request_info()->client_certs.size());
223
224 for (size_t i = 0; i < cert_request_info()->client_certs.size(); ++i) {
225 net::X509Certificate::OSCertHandle cert =
226 cert_request_info()->client_certs[i]->os_cert_handle();
227
228 details_strings_.push_back(FormatDetailsText(cert));
229
230 gtk_combo_box_append_text(
231 GTK_COMBO_BOX(cert_combo_box_),
232 FormatComboBoxText(cert, nicknames[i]).c_str());
233 }
234
235 // Auto-select the first cert.
236 gtk_combo_box_set_active(GTK_COMBO_BOX(cert_combo_box_), 0);
237 }
238
239 net::X509Certificate* SSLClientCertificateSelector::GetSelectedCert() {
240 int selected = gtk_combo_box_get_active(GTK_COMBO_BOX(cert_combo_box_));
241 if (selected >= 0 &&
242 selected < static_cast<int>(
243 cert_request_info()->client_certs.size()))
244 return cert_request_info()->client_certs[selected].get();
245 return NULL;
246 }
247
248 // static
249 std::string SSLClientCertificateSelector::FormatComboBoxText(
250 net::X509Certificate::OSCertHandle cert, const std::string& nickname) {
251 std::string rv(nickname);
252 rv += " [";
253 rv += x509_certificate_model::GetSerialNumberHexified(cert, std::string());
254 rv += ']';
255 return rv;
256 }
257
258 // static
259 std::string SSLClientCertificateSelector::FormatDetailsText(
260 net::X509Certificate::OSCertHandle cert) {
261 std::string rv;
262
263 rv += l10n_util::GetStringFUTF8(
264 IDS_CERT_SUBJECTNAME_FORMAT,
265 base::UTF8ToUTF16(x509_certificate_model::GetSubjectName(cert)));
266
267 rv += "\n ";
268 rv += l10n_util::GetStringFUTF8(
269 IDS_CERT_SERIAL_NUMBER_FORMAT,
270 base::UTF8ToUTF16(x509_certificate_model::GetSerialNumberHexified(
271 cert, std::string())));
272
273 base::Time issued, expires;
274 if (x509_certificate_model::GetTimes(cert, &issued, &expires)) {
275 base::string16 issued_str = base::TimeFormatShortDateAndTime(issued);
276 base::string16 expires_str = base::TimeFormatShortDateAndTime(expires);
277 rv += "\n ";
278 rv += l10n_util::GetStringFUTF8(IDS_CERT_VALIDITY_RANGE_FORMAT,
279 issued_str, expires_str);
280 }
281
282 std::vector<std::string> usages;
283 x509_certificate_model::GetUsageStrings(cert, &usages);
284 if (usages.size()) {
285 rv += "\n ";
286 rv += l10n_util::GetStringFUTF8(IDS_CERT_X509_EXTENDED_KEY_USAGE_FORMAT,
287 base::UTF8ToUTF16(JoinString(usages, ',')));
288 }
289
290 std::string key_usage_str = x509_certificate_model::GetKeyUsageString(cert);
291 if (!key_usage_str.empty()) {
292 rv += "\n ";
293 rv += l10n_util::GetStringFUTF8(IDS_CERT_X509_KEY_USAGE_FORMAT,
294 base::UTF8ToUTF16(key_usage_str));
295 }
296
297 std::vector<std::string> email_addresses;
298 x509_certificate_model::GetEmailAddresses(cert, &email_addresses);
299 if (email_addresses.size()) {
300 rv += "\n ";
301 rv += l10n_util::GetStringFUTF8(
302 IDS_CERT_EMAIL_ADDRESSES_FORMAT,
303 base::UTF8ToUTF16(JoinString(email_addresses, ',')));
304 }
305
306 rv += '\n';
307 rv += l10n_util::GetStringFUTF8(
308 IDS_CERT_ISSUERNAME_FORMAT,
309 base::UTF8ToUTF16(x509_certificate_model::GetIssuerName(cert)));
310
311 base::string16 token(
312 base::UTF8ToUTF16(x509_certificate_model::GetTokenName(cert)));
313 if (!token.empty()) {
314 rv += '\n';
315 rv += l10n_util::GetStringFUTF8(IDS_CERT_TOKEN_FORMAT, token);
316 }
317
318 return rv;
319 }
320
321 void SSLClientCertificateSelector::Unlocked() {
322 // TODO(mattm): refactor so we don't need to call GetSelectedCert again.
323 net::X509Certificate* cert = GetSelectedCert();
324 CertificateSelected(cert);
325 DCHECK(window_);
326 gtk_widget_destroy(window_);
327 }
328
329 void SSLClientCertificateSelector::OnComboBoxChanged(GtkWidget* combo_box) {
330 int selected = gtk_combo_box_get_active(
331 GTK_COMBO_BOX(cert_combo_box_));
332 if (selected < 0)
333 return;
334 gtk_text_buffer_set_text(cert_details_buffer_,
335 details_strings_[selected].c_str(),
336 details_strings_[selected].size());
337 }
338
339 void SSLClientCertificateSelector::OnViewClicked(GtkWidget* button) {
340 net::X509Certificate* cert = GetSelectedCert();
341 if (cert) {
342 GtkWidget* toplevel = gtk_widget_get_toplevel(root_widget_.get());
343 ShowCertificateViewer(web_contents_, GTK_WINDOW(toplevel), cert);
344 }
345 }
346
347 void SSLClientCertificateSelector::OnCancelClicked(GtkWidget* button) {
348 CertificateSelected(NULL);
349 DCHECK(window_);
350 gtk_widget_destroy(window_);
351 }
352
353 void SSLClientCertificateSelector::OnOkClicked(GtkWidget* button) {
354 // Remove the observer before we try unlocking, otherwise we might act on a
355 // notification while waiting for the unlock dialog, causing us to delete
356 // ourself before the Unlocked callback gets called.
357 StopObserving();
358
359 #if defined(USE_NSS)
360 GtkWidget* toplevel = gtk_widget_get_toplevel(root_widget_.get());
361 net::X509Certificate* cert = GetSelectedCert();
362
363 chrome::UnlockCertSlotIfNecessary(
364 cert,
365 chrome::kCryptoModulePasswordClientAuth,
366 cert_request_info()->host_and_port,
367 GTK_WINDOW(toplevel),
368 base::Bind(&SSLClientCertificateSelector::Unlocked,
369 base::Unretained(this)));
370 #else
371 Unlocked();
372 #endif
373 }
374
375 void SSLClientCertificateSelector::OnPromptShown(GtkWidget* widget,
376 GtkWidget* previous_toplevel) {
377 if (!root_widget_.get() ||
378 !gtk_widget_is_toplevel(gtk_widget_get_toplevel(root_widget_.get())))
379 return;
380 gtk_widget_set_can_default(select_button_, TRUE);
381 gtk_widget_grab_default(select_button_);
382 }
383
384 void SSLClientCertificateSelector::OnDestroy(GtkWidget* widget) {
385 // The dialog was closed by escape key.
386 StopObserving();
387 CertificateSelected(NULL);
388 delete this;
389 }
390
391 } // namespace
392
393 namespace chrome {
394
395 void ShowSSLClientCertificateSelector(
396 content::WebContents* contents,
397 const net::HttpNetworkSession* network_session,
398 net::SSLCertRequestInfo* cert_request_info,
399 const base::Callback<void(net::X509Certificate*)>& callback) {
400 DCHECK_CURRENTLY_ON(BrowserThread::UI);
401 (new SSLClientCertificateSelector(
402 contents, network_session, cert_request_info, callback))->Show();
403 }
404
405 } // namespace chrome
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698