OLD | NEW |
| (Empty) |
1 // Copyright (c) 2010 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/gtk/certificate_manager.h" | |
6 | |
7 #include <cert.h> | |
8 #include <gtk/gtk.h> | |
9 #include <pk11pub.h> | |
10 | |
11 #include <map> | |
12 #include <string> | |
13 | |
14 #include "app/gtk_signal.h" | |
15 #include "app/l10n_util.h" | |
16 #include "app/l10n_util_collator.h" | |
17 #include "base/gtk_util.h" | |
18 #include "base/i18n/time_formatting.h" | |
19 #include "base/nss_util.h" | |
20 #include "chrome/browser/browser_process.h" | |
21 #include "chrome/browser/gtk/certificate_viewer.h" | |
22 #include "chrome/browser/gtk/gtk_util.h" | |
23 #include "chrome/browser/prefs/pref_member.h" | |
24 #include "chrome/browser/prefs/pref_service.h" | |
25 #include "chrome/browser/profile.h" | |
26 #include "chrome/common/pref_names.h" | |
27 #include "chrome/third_party/mozilla_security_manager/nsNSSCertHelper.h" | |
28 #include "chrome/third_party/mozilla_security_manager/nsNSSCertificate.h" | |
29 #include "grit/generated_resources.h" | |
30 | |
31 // PSM = Mozilla's Personal Security Manager. | |
32 namespace psm = mozilla_security_manager; | |
33 | |
34 namespace { | |
35 | |
36 // Convert a char* return value from NSS into a std::string and free the NSS | |
37 // memory. If the arg is NULL, an empty string will be returned instead. | |
38 std::string Stringize(char* nss_text) { | |
39 std::string s; | |
40 if (nss_text) { | |
41 s = nss_text; | |
42 PORT_Free(nss_text); | |
43 } | |
44 return s; | |
45 } | |
46 | |
47 //////////////////////////////////////////////////////////////////////////////// | |
48 // CertificatePage class definition. | |
49 | |
50 class CertificatePage { | |
51 public: | |
52 explicit CertificatePage(net::CertType type); | |
53 virtual ~CertificatePage() {} | |
54 | |
55 void PopulateTree(CERTCertList* cert_list); | |
56 | |
57 // Get the top-level widget of this page. | |
58 GtkWidget* widget() { return vbox_; } | |
59 | |
60 private: | |
61 // Columns of the tree store. | |
62 enum { | |
63 CERT_NAME, | |
64 CERT_SECURITY_DEVICE, | |
65 CERT_SERIAL_NUMBER, | |
66 CERT_EXPIRES_ON, | |
67 CERT_EXPIRES_ON_INT, | |
68 CERT_ADDRESS, | |
69 CERT_POINTER, | |
70 CERT_STORE_NUM_COLUMNS | |
71 }; | |
72 | |
73 gint LocaleSortFunc(GtkTreeModel* model, GtkTreeIter* a, GtkTreeIter* b, | |
74 int col); | |
75 | |
76 // Gtk event callbacks. | |
77 CHROMEG_CALLBACK_2(CertificatePage, gint, SortNameFunc, GtkTreeModel*, | |
78 GtkTreeIter*, GtkTreeIter*); | |
79 CHROMEG_CALLBACK_2(CertificatePage, gint, SortDeviceFunc, GtkTreeModel*, | |
80 GtkTreeIter*, GtkTreeIter*); | |
81 CHROMEG_CALLBACK_0(CertificatePage, void, OnSelectionChanged, | |
82 GtkTreeSelection*); | |
83 CHROMEGTK_CALLBACK_0(CertificatePage, void, OnViewClicked); | |
84 | |
85 net::CertType type_; | |
86 | |
87 // The top-level widget of this page. | |
88 GtkWidget* vbox_; | |
89 | |
90 GtkWidget* tree_; | |
91 GtkTreeStore* store_; | |
92 GtkTreeSelection* selection_; | |
93 scoped_ptr<icu::Collator> collator_; | |
94 | |
95 GtkWidget* view_button_; | |
96 }; | |
97 | |
98 //////////////////////////////////////////////////////////////////////////////// | |
99 // CertificatePage implementation. | |
100 | |
101 CertificatePage::CertificatePage(net::CertType type) : type_(type) { | |
102 vbox_ = gtk_vbox_new(FALSE, gtk_util::kControlSpacing); | |
103 gtk_container_set_border_width(GTK_CONTAINER(vbox_), | |
104 gtk_util::kContentAreaBorder); | |
105 | |
106 static const int kDescriptionIds[] = { | |
107 IDS_CERT_MANAGER_USER_TREE_DESCRIPTION, | |
108 IDS_CERT_MANAGER_OTHER_PEOPLE_TREE_DESCRIPTION, | |
109 IDS_CERT_MANAGER_SERVER_TREE_DESCRIPTION, | |
110 IDS_CERT_MANAGER_AUTHORITIES_TREE_DESCRIPTION, | |
111 IDS_CERT_MANAGER_UNKNOWN_TREE_DESCRIPTION, | |
112 }; | |
113 DCHECK_EQ(arraysize(kDescriptionIds), | |
114 static_cast<size_t>(net::NUM_CERT_TYPES)); | |
115 GtkWidget* description_label = gtk_label_new(l10n_util::GetStringUTF8( | |
116 kDescriptionIds[type]).c_str()); | |
117 gtk_util::LeftAlignMisc(description_label); | |
118 gtk_box_pack_start(GTK_BOX(vbox_), description_label, FALSE, FALSE, 0); | |
119 | |
120 store_ = gtk_tree_store_new(CERT_STORE_NUM_COLUMNS, | |
121 G_TYPE_STRING, | |
122 G_TYPE_STRING, | |
123 G_TYPE_STRING, | |
124 G_TYPE_STRING, | |
125 G_TYPE_INT64, | |
126 G_TYPE_STRING, | |
127 G_TYPE_POINTER); | |
128 tree_ = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store_)); | |
129 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_), TRUE); | |
130 selection_ = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_)); | |
131 gtk_tree_selection_set_mode(selection_, GTK_SELECTION_SINGLE); | |
132 g_signal_connect(selection_, "changed", G_CALLBACK(OnSelectionChangedThunk), | |
133 this); | |
134 | |
135 GtkTreeViewColumn* name_col = gtk_tree_view_column_new_with_attributes( | |
136 l10n_util::GetStringUTF8(IDS_CERT_MANAGER_NAME_COLUMN_LABEL).c_str(), | |
137 gtk_cell_renderer_text_new(), | |
138 "text", CERT_NAME, | |
139 NULL); | |
140 gtk_tree_view_column_set_sort_column_id(name_col, CERT_NAME); | |
141 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_), name_col); | |
142 | |
143 if (type == net::USER_CERT || type == net::CA_CERT || | |
144 type == net::UNKNOWN_CERT) { | |
145 GtkTreeViewColumn* device_col = gtk_tree_view_column_new_with_attributes( | |
146 l10n_util::GetStringUTF8( | |
147 IDS_CERT_MANAGER_DEVICE_COLUMN_LABEL).c_str(), | |
148 gtk_cell_renderer_text_new(), | |
149 "text", CERT_SECURITY_DEVICE, | |
150 NULL); | |
151 gtk_tree_view_column_set_sort_column_id(device_col, CERT_SECURITY_DEVICE); | |
152 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_), device_col); | |
153 } | |
154 | |
155 if (type == net::USER_CERT) { | |
156 GtkTreeViewColumn* serial_col = gtk_tree_view_column_new_with_attributes( | |
157 l10n_util::GetStringUTF8( | |
158 IDS_CERT_MANAGER_SERIAL_NUMBER_COLUMN_LABEL).c_str(), | |
159 gtk_cell_renderer_text_new(), | |
160 "text", CERT_SERIAL_NUMBER, | |
161 NULL); | |
162 gtk_tree_view_column_set_sort_column_id(serial_col, CERT_SERIAL_NUMBER); | |
163 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_), serial_col); | |
164 } | |
165 | |
166 if (type == net::USER_CERT || type == net::EMAIL_CERT || | |
167 type == net::SERVER_CERT) { | |
168 GtkTreeViewColumn* expires_col = gtk_tree_view_column_new_with_attributes( | |
169 l10n_util::GetStringUTF8( | |
170 IDS_CERT_MANAGER_EXPIRES_COLUMN_LABEL).c_str(), | |
171 gtk_cell_renderer_text_new(), | |
172 "text", CERT_EXPIRES_ON, | |
173 NULL); | |
174 gtk_tree_view_column_set_sort_column_id(expires_col, CERT_EXPIRES_ON_INT); | |
175 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_), expires_col); | |
176 } | |
177 | |
178 if (type == net::EMAIL_CERT) { | |
179 GtkTreeViewColumn* addr_col = gtk_tree_view_column_new_with_attributes( | |
180 l10n_util::GetStringUTF8( | |
181 IDS_CERT_MANAGER_EMAIL_ADDRESS_COLUMN_LABEL).c_str(), | |
182 gtk_cell_renderer_text_new(), | |
183 "text", CERT_ADDRESS, | |
184 NULL); | |
185 gtk_tree_view_column_set_sort_column_id(addr_col, CERT_ADDRESS); | |
186 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_), addr_col); | |
187 } | |
188 | |
189 UErrorCode error = U_ZERO_ERROR; | |
190 collator_.reset( | |
191 icu::Collator::createInstance( | |
192 icu::Locale(g_browser_process->GetApplicationLocale().c_str()), | |
193 error)); | |
194 if (U_FAILURE(error)) | |
195 collator_.reset(NULL); | |
196 if (collator_ != NULL) { | |
197 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store_), CERT_NAME, | |
198 SortNameFuncThunk, this, NULL); | |
199 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store_), | |
200 CERT_SECURITY_DEVICE, SortDeviceFuncThunk, | |
201 this, NULL); | |
202 } | |
203 | |
204 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store_), CERT_NAME, | |
205 GTK_SORT_ASCENDING); | |
206 | |
207 GtkWidget* scroll_window = gtk_scrolled_window_new(NULL, NULL); | |
208 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_window), | |
209 GTK_POLICY_AUTOMATIC, | |
210 GTK_POLICY_AUTOMATIC); | |
211 gtk_scrolled_window_set_shadow_type( | |
212 GTK_SCROLLED_WINDOW(scroll_window), GTK_SHADOW_ETCHED_IN); | |
213 gtk_container_add(GTK_CONTAINER(scroll_window), tree_); | |
214 gtk_box_pack_start(GTK_BOX(vbox_), scroll_window, TRUE, TRUE, 0); | |
215 | |
216 GtkWidget* button_box = gtk_hbox_new(FALSE, gtk_util::kControlSpacing); | |
217 gtk_box_pack_start(GTK_BOX(vbox_), button_box, FALSE, FALSE, 0); | |
218 | |
219 view_button_ = gtk_button_new_with_mnemonic( | |
220 gtk_util::ConvertAcceleratorsFromWindowsStyle( | |
221 l10n_util::GetStringUTF8( | |
222 IDS_CERT_MANAGER_VIEW_CERT_BUTTON)).c_str()); | |
223 gtk_widget_set_sensitive(view_button_, FALSE); | |
224 g_signal_connect(view_button_, "clicked", | |
225 G_CALLBACK(OnViewClickedThunk), this); | |
226 gtk_box_pack_start(GTK_BOX(button_box), view_button_, FALSE, FALSE, 0); | |
227 | |
228 // TODO(mattm): Add buttons for import, export, delete, etc | |
229 } | |
230 | |
231 void CertificatePage::PopulateTree(CERTCertList* cert_list) { | |
232 DCHECK(gtk_tree_model_get_flags(GTK_TREE_MODEL(store_)) & | |
233 GTK_TREE_MODEL_ITERS_PERSIST); | |
234 | |
235 typedef std::map<std::string, GtkTreeIter> OrgTreeMap; | |
236 OrgTreeMap org_tree_map; | |
237 | |
238 CERTCertListNode* node; | |
239 for (node = CERT_LIST_HEAD(cert_list); | |
240 !CERT_LIST_END(node, cert_list); | |
241 node = CERT_LIST_NEXT(node)) { | |
242 CERTCertificate* cert = node->cert; | |
243 net::CertType type = psm::GetCertType(cert); | |
244 if (type == type_) { | |
245 std::string org = Stringize(CERT_GetOrgName(&cert->subject)); | |
246 if (org.empty()) | |
247 org = Stringize(CERT_GetCommonName(&cert->subject)); | |
248 OrgTreeMap::iterator org_tree_map_iter = org_tree_map.find(org); | |
249 if (org_tree_map_iter == org_tree_map.end()) { | |
250 GtkTreeIter iter; | |
251 gtk_tree_store_append(store_, &iter, NULL); | |
252 gtk_tree_store_set(store_, &iter, CERT_NAME, org.c_str(), -1); | |
253 org_tree_map_iter = org_tree_map.insert(std::make_pair(org, | |
254 iter)).first; | |
255 } | |
256 std::string name = psm::ProcessIDN( | |
257 Stringize(CERT_GetCommonName(&cert->subject))); | |
258 if (name.empty() && cert->nickname) { | |
259 name = cert->nickname; | |
260 // Hack copied from mozilla: Cut off text before first :, which seems to | |
261 // just be the token name. | |
262 size_t colon_pos = name.find(':'); | |
263 if (colon_pos != std::string::npos) | |
264 name = name.substr(colon_pos + 1); | |
265 } | |
266 GtkTreeIter iter; | |
267 gtk_tree_store_append(store_, &iter, &org_tree_map_iter->second); | |
268 gtk_tree_store_set(store_, &iter, | |
269 CERT_NAME, name.c_str(), | |
270 CERT_SECURITY_DEVICE, | |
271 psm::GetCertTokenName(cert).c_str(), | |
272 CERT_SERIAL_NUMBER, | |
273 Stringize(CERT_Hexify( | |
274 &cert->serialNumber, TRUE)).c_str(), | |
275 CERT_ADDRESS, cert->emailAddr, | |
276 CERT_POINTER, cert, | |
277 -1); | |
278 | |
279 PRTime issued, expires; | |
280 if (CERT_GetCertTimes(cert, &issued, &expires) == SECSuccess) { | |
281 gtk_tree_store_set(store_, &iter, | |
282 CERT_EXPIRES_ON, | |
283 WideToUTF8(base::TimeFormatShortDateNumeric( | |
284 base::PRTimeToBaseTime(expires))).c_str(), | |
285 CERT_EXPIRES_ON_INT, expires, | |
286 -1); | |
287 } | |
288 } | |
289 } | |
290 | |
291 gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_)); | |
292 } | |
293 | |
294 gint CertificatePage::LocaleSortFunc(GtkTreeModel* model, | |
295 GtkTreeIter* a, | |
296 GtkTreeIter* b, | |
297 int col) { | |
298 gchar* value1 = NULL; | |
299 gchar* value2 = NULL; | |
300 gtk_tree_model_get(model, a, col, &value1, -1); | |
301 gtk_tree_model_get(model, b, col, &value2, -1); | |
302 if (!value1 || !value2) { | |
303 if (value1) | |
304 return 1; | |
305 if (value2) | |
306 return -1; | |
307 return 0; | |
308 } | |
309 | |
310 return l10n_util::CompareStringWithCollator(collator_.get(), | |
311 UTF8ToWide(value1), | |
312 UTF8ToWide(value2)); | |
313 } | |
314 | |
315 gint CertificatePage::SortNameFunc(GtkTreeModel* model, GtkTreeIter* a, | |
316 GtkTreeIter* b) { | |
317 return LocaleSortFunc(model, a, b, CERT_NAME); | |
318 } | |
319 | |
320 gint CertificatePage::SortDeviceFunc(GtkTreeModel* model, GtkTreeIter* a, | |
321 GtkTreeIter* b) { | |
322 return LocaleSortFunc(model, a, b, CERT_SECURITY_DEVICE); | |
323 } | |
324 | |
325 void CertificatePage::OnSelectionChanged(GtkTreeSelection* selection) { | |
326 CERTCertificate* cert = NULL; | |
327 GtkTreeIter iter; | |
328 GtkTreeModel* model; | |
329 if (gtk_tree_selection_get_selected(selection_, &model, &iter)) | |
330 gtk_tree_model_get(model, &iter, CERT_POINTER, &cert, -1); | |
331 | |
332 gtk_widget_set_sensitive(view_button_, cert ? TRUE : FALSE); | |
333 } | |
334 | |
335 void CertificatePage::OnViewClicked(GtkWidget* button) { | |
336 GtkTreeIter iter; | |
337 GtkTreeModel* model; | |
338 if (!gtk_tree_selection_get_selected(selection_, &model, &iter)) | |
339 return; | |
340 | |
341 CERTCertificate* cert = NULL; | |
342 gtk_tree_model_get(model, &iter, CERT_POINTER, &cert, -1); | |
343 if (cert) | |
344 ShowCertificateViewer(GTK_WINDOW(gtk_widget_get_toplevel(widget())), cert); | |
345 } | |
346 | |
347 //////////////////////////////////////////////////////////////////////////////// | |
348 // CertificateManager class definition. | |
349 | |
350 class CertificateManager { | |
351 public: | |
352 explicit CertificateManager(gfx::NativeWindow parent, Profile* profile); | |
353 virtual ~CertificateManager(); | |
354 | |
355 // Shows the Tab corresponding to the specified |page|. | |
356 void ShowCertificatePage(CertificateManagerPage page); | |
357 | |
358 private: | |
359 CHROMEGTK_CALLBACK_2(CertificateManager, void, OnSwitchPage, | |
360 GtkNotebookPage*, guint); | |
361 | |
362 CERTCertList* cert_list_; | |
363 | |
364 CertificatePage user_page_; | |
365 CertificatePage email_page_; | |
366 CertificatePage server_page_; | |
367 CertificatePage ca_page_; | |
368 CertificatePage unknown_page_; | |
369 | |
370 GtkWidget* dialog_; | |
371 | |
372 GtkWidget* notebook_; | |
373 | |
374 // The last page the user was on when they opened the CertificateManager | |
375 // window. | |
376 IntegerPrefMember last_selected_page_; | |
377 | |
378 DISALLOW_COPY_AND_ASSIGN(CertificateManager); | |
379 }; | |
380 | |
381 //////////////////////////////////////////////////////////////////////////////// | |
382 // CertificateManager implementation. | |
383 | |
384 void OnDestroy(GtkDialog* dialog, CertificateManager* cert_manager) { | |
385 delete cert_manager; | |
386 } | |
387 | |
388 CertificateManager::CertificateManager(gfx::NativeWindow parent, | |
389 Profile* profile) | |
390 : user_page_(net::USER_CERT), | |
391 email_page_(net::EMAIL_CERT), | |
392 server_page_(net::SERVER_CERT), | |
393 ca_page_(net::CA_CERT), | |
394 unknown_page_(net::UNKNOWN_CERT) { | |
395 // We don't need to observe changes in this value. | |
396 last_selected_page_.Init(prefs::kCertificateManagerWindowLastTabIndex, | |
397 profile->GetPrefs(), NULL); | |
398 | |
399 dialog_ = gtk_dialog_new_with_buttons( | |
400 l10n_util::GetStringUTF8(IDS_CERTIFICATE_MANAGER_TITLE).c_str(), | |
401 parent, | |
402 // Non-modal. | |
403 GTK_DIALOG_NO_SEPARATOR, | |
404 GTK_STOCK_CLOSE, | |
405 GTK_RESPONSE_CLOSE, | |
406 NULL); | |
407 gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog_)->vbox), | |
408 gtk_util::kContentAreaSpacing); | |
409 gtk_window_set_default_size(GTK_WINDOW(dialog_), 600, 440); | |
410 | |
411 notebook_ = gtk_notebook_new(); | |
412 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog_)->vbox), notebook_); | |
413 | |
414 gtk_notebook_append_page( | |
415 GTK_NOTEBOOK(notebook_), | |
416 user_page_.widget(), | |
417 gtk_label_new_with_mnemonic( | |
418 l10n_util::GetStringUTF8( | |
419 IDS_CERT_MANAGER_PERSONAL_CERTS_TAB_LABEL).c_str())); | |
420 | |
421 gtk_notebook_append_page( | |
422 GTK_NOTEBOOK(notebook_), | |
423 email_page_.widget(), | |
424 gtk_label_new_with_mnemonic( | |
425 l10n_util::GetStringUTF8( | |
426 IDS_CERT_MANAGER_OTHER_PEOPLES_CERTS_TAB_LABEL).c_str())); | |
427 | |
428 gtk_notebook_append_page( | |
429 GTK_NOTEBOOK(notebook_), | |
430 server_page_.widget(), | |
431 gtk_label_new_with_mnemonic( | |
432 l10n_util::GetStringUTF8( | |
433 IDS_CERT_MANAGER_SERVER_CERTS_TAB_LABEL).c_str())); | |
434 | |
435 gtk_notebook_append_page( | |
436 GTK_NOTEBOOK(notebook_), | |
437 ca_page_.widget(), | |
438 gtk_label_new_with_mnemonic( | |
439 l10n_util::GetStringUTF8( | |
440 IDS_CERT_MANAGER_CERT_AUTHORITIES_TAB_LABEL).c_str())); | |
441 | |
442 gtk_notebook_append_page( | |
443 GTK_NOTEBOOK(notebook_), | |
444 unknown_page_.widget(), | |
445 gtk_label_new_with_mnemonic( | |
446 l10n_util::GetStringUTF8( | |
447 IDS_CERT_MANAGER_UNKNOWN_TAB_LABEL).c_str())); | |
448 | |
449 cert_list_ = PK11_ListCerts(PK11CertListUnique, NULL); | |
450 user_page_.PopulateTree(cert_list_); | |
451 email_page_.PopulateTree(cert_list_); | |
452 server_page_.PopulateTree(cert_list_); | |
453 ca_page_.PopulateTree(cert_list_); | |
454 unknown_page_.PopulateTree(cert_list_); | |
455 | |
456 // Need to show the notebook before connecting switch-page signal, otherwise | |
457 // we'll immediately get a signal switching to page 0 and overwrite our | |
458 // last_selected_page_ value. | |
459 gtk_util::ShowDialogWithLocalizedSize(dialog_, -1, -1, true); | |
460 | |
461 g_signal_connect(notebook_, "switch-page", | |
462 G_CALLBACK(OnSwitchPageThunk), this); | |
463 | |
464 g_signal_connect(dialog_, "response", G_CALLBACK(gtk_widget_destroy), NULL); | |
465 g_signal_connect(dialog_, "destroy", G_CALLBACK(OnDestroy), this); | |
466 } | |
467 | |
468 CertificateManager::~CertificateManager() { | |
469 CERT_DestroyCertList(cert_list_); | |
470 } | |
471 | |
472 void CertificateManager::OnSwitchPage(GtkWidget* notebook, | |
473 GtkNotebookPage* page, | |
474 guint page_num) { | |
475 int index = static_cast<int>(page_num); | |
476 DCHECK(index > CERT_MANAGER_PAGE_DEFAULT && index < CERT_MANAGER_PAGE_COUNT); | |
477 last_selected_page_.SetValue(index); | |
478 } | |
479 | |
480 void CertificateManager::ShowCertificatePage(CertificateManagerPage page) { | |
481 // Bring options window to front if it already existed and isn't already | |
482 // in front | |
483 gtk_window_present_with_time(GTK_WINDOW(dialog_), | |
484 gtk_get_current_event_time()); | |
485 | |
486 if (page == CERT_MANAGER_PAGE_DEFAULT) { | |
487 // Remember the last visited page from local state. | |
488 page = static_cast<CertificateManagerPage>(last_selected_page_.GetValue()); | |
489 if (page == CERT_MANAGER_PAGE_DEFAULT) | |
490 page = CERT_MANAGER_PAGE_USER; | |
491 } | |
492 // If the page number is out of bounds, reset to the first tab. | |
493 if (page < 0 || page >= gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook_))) | |
494 page = CERT_MANAGER_PAGE_USER; | |
495 | |
496 gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook_), page); | |
497 } | |
498 | |
499 } // namespace | |
500 | |
501 namespace certificate_manager_util { | |
502 | |
503 void RegisterUserPrefs(PrefService* prefs) { | |
504 prefs->RegisterIntegerPref(prefs::kCertificateManagerWindowLastTabIndex, 0); | |
505 } | |
506 | |
507 } // namespace certificate_manager_util | |
508 | |
509 void ShowCertificateManager(gfx::NativeWindow parent, Profile* profile, | |
510 CertificateManagerPage page) { | |
511 base::EnsureNSSInit(); | |
512 CertificateManager* manager = new CertificateManager(parent, profile); | |
513 manager->ShowCertificatePage(page); | |
514 } | |
OLD | NEW |