certificate_viewer.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
1// Copyright (c) 2011 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/gtk/certificate_viewer.h"
6
7#include <gtk/gtk.h>
8
9#include <algorithm>
10#include <vector>
11
12#include "base/i18n/time_formatting.h"
13#include "base/nss_util.h"
14#include "base/scoped_ptr.h"
15#include "base/string_number_conversions.h"
16#include "base/time.h"
17#include "base/utf_string_conversions.h"
18#include "chrome/browser/ui/gtk/certificate_dialogs.h"
19#include "chrome/browser/ui/gtk/gtk_util.h"
20#include "chrome/common/net/x509_certificate_model.h"
21#include "grit/generated_resources.h"
22#include "net/base/x509_certificate.h"
23#include "ui/base/l10n/l10n_util.h"
24#include "ui/gfx/gtk_util.h"
25
26namespace {
27
28const char kDetailsFontFamily[] = "monospace";
29
30////////////////////////////////////////////////////////////////////////////////
31// Gtk utility functions.
32
33void AddTitle(GtkTable* table, int row, const std::string& text) {
34  gtk_table_attach_defaults(table,
35                            gtk_util::CreateBoldLabel(text),
36                            0, 2,
37                            row, row + 1);
38}
39
40void AddKeyValue(GtkTable* table, int row, const std::string& text,
41                 const std::string& value) {
42  gtk_table_attach_defaults(
43      table,
44      gtk_util::IndentWidget(
45          gtk_util::LeftAlignMisc(gtk_label_new(text.c_str()))),
46      0, 1, row, row + 1);
47  gtk_table_attach_defaults(
48      table,
49      gtk_util::LeftAlignMisc(gtk_label_new(value.c_str())),
50      1, 2, row, row + 1);
51}
52
53////////////////////////////////////////////////////////////////////////////////
54// CertificateViewer class definition.
55
56class CertificateViewer {
57 public:
58  CertificateViewer(gfx::NativeWindow parent,
59                    const net::X509Certificate::OSCertHandles& cert_chain_list);
60  ~CertificateViewer();
61
62  void InitGeneralPage();
63  void InitDetailsPage();
64
65  void Show();
66
67 private:
68  // Indices and column count for the certificate chain hierarchy tree store.
69  enum {
70    HIERARCHY_NAME,
71    HIERARCHY_OBJECT,
72    HIERARCHY_INDEX,
73    HIERARCHY_COLUMNS
74  };
75
76  // Indices and column count for the certificate fields tree store.
77  enum {
78    FIELDS_NAME,
79    FIELDS_VALUE,
80    FIELDS_COLUMNS
81  };
82
83  // Fill the tree store with the certificate hierarchy, and set |leaf| to the
84  // iter of the leaf node.
85  void FillHierarchyStore(GtkTreeStore* hierarchy_store,
86                          GtkTreeIter* leaf) const;
87
88  // Fill the tree store with the details of the given certificate.
89  static void FillTreeStoreWithCertFields(
90      GtkTreeStore* store, net::X509Certificate::OSCertHandle cert);
91
92  // Create a tree store filled with the details of the given certificate.
93  static GtkTreeStore* CreateFieldsTreeStore(
94      net::X509Certificate::OSCertHandle cert);
95
96  // Callbacks for user selecting elements in the trees.
97  static void OnHierarchySelectionChanged(GtkTreeSelection* selection,
98                                          CertificateViewer* viewer);
99  static void OnFieldsSelectionChanged(GtkTreeSelection* selection,
100                                       CertificateViewer* viewer);
101
102  // Callback for export button.
103  static void OnExportClicked(GtkButton *button, CertificateViewer* viewer);
104
105  // The certificate hierarchy (leaf cert first).
106  net::X509Certificate::OSCertHandles cert_chain_list_;
107
108  GtkWidget* dialog_;
109  GtkWidget* notebook_;
110  GtkWidget* general_page_vbox_;
111  GtkWidget* details_page_vbox_;
112  GtkTreeSelection* hierarchy_selection_;
113  GtkWidget* fields_tree_;
114  GtkTextBuffer* field_value_buffer_;
115  GtkWidget* export_button_;
116
117  DISALLOW_COPY_AND_ASSIGN(CertificateViewer);
118};
119
120////////////////////////////////////////////////////////////////////////////////
121// CertificateViewer implementation.
122
123// Close button callback.
124void OnDialogResponse(GtkDialog* dialog, gint response_id,
125                      gpointer user_data) {
126  // "Close" was clicked.
127  gtk_widget_destroy(GTK_WIDGET(dialog));
128}
129
130void OnDestroy(GtkDialog* dialog, CertificateViewer* cert_viewer) {
131  delete cert_viewer;
132}
133
134CertificateViewer::CertificateViewer(
135    gfx::NativeWindow parent,
136    const net::X509Certificate::OSCertHandles& cert_chain_list)
137    : cert_chain_list_(cert_chain_list) {
138  dialog_ = gtk_dialog_new_with_buttons(
139      l10n_util::GetStringFUTF8(
140          IDS_CERT_INFO_DIALOG_TITLE,
141          UTF8ToUTF16(
142              x509_certificate_model::GetTitle(
143                  cert_chain_list_.front()))).c_str(),
144      parent,
145      // Non-modal.
146      GTK_DIALOG_NO_SEPARATOR,
147      GTK_STOCK_CLOSE,
148      GTK_RESPONSE_CLOSE,
149      NULL);
150  gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog_)->vbox),
151                      gtk_util::kContentAreaSpacing);
152
153  x509_certificate_model::RegisterDynamicOids();
154  InitGeneralPage();
155  InitDetailsPage();
156
157  notebook_ = gtk_notebook_new();
158  gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog_)->vbox), notebook_);
159
160  gtk_notebook_append_page(
161      GTK_NOTEBOOK(notebook_),
162      general_page_vbox_,
163      gtk_label_new_with_mnemonic(
164          gfx::ConvertAcceleratorsFromWindowsStyle(
165              l10n_util::GetStringUTF8(
166                  IDS_CERT_INFO_GENERAL_TAB_LABEL)).c_str()));
167
168  gtk_notebook_append_page(
169      GTK_NOTEBOOK(notebook_),
170      details_page_vbox_,
171      gtk_label_new_with_mnemonic(
172          gfx::ConvertAcceleratorsFromWindowsStyle(
173              l10n_util::GetStringUTF8(
174                  IDS_CERT_INFO_DETAILS_TAB_LABEL)).c_str()));
175
176  g_signal_connect(dialog_, "response", G_CALLBACK(OnDialogResponse), NULL);
177  g_signal_connect(dialog_, "destroy", G_CALLBACK(OnDestroy), this);
178}
179
180CertificateViewer::~CertificateViewer() {
181  x509_certificate_model::DestroyCertChain(&cert_chain_list_);
182}
183
184void CertificateViewer::InitGeneralPage() {
185  net::X509Certificate::OSCertHandle cert = cert_chain_list_.front();
186  general_page_vbox_ = gtk_vbox_new(FALSE, gtk_util::kContentAreaSpacing);
187  gtk_container_set_border_width(GTK_CONTAINER(general_page_vbox_),
188                                 gtk_util::kContentAreaBorder);
189
190  GtkWidget* uses_vbox = gtk_vbox_new(FALSE, gtk_util::kControlSpacing);
191  gtk_box_pack_start(GTK_BOX(general_page_vbox_), uses_vbox, FALSE, FALSE, 0);
192  gtk_box_pack_start(
193      GTK_BOX(uses_vbox),
194      gtk_util::CreateBoldLabel(
195          l10n_util::GetStringUTF8(IDS_CERT_INFO_VERIFIED_USAGES_GROUP)),
196      FALSE, FALSE, 0);
197
198  std::vector<std::string> usages;
199  x509_certificate_model::GetUsageStrings(cert, &usages);
200  for (size_t i = 0; i < usages.size(); ++i)
201    gtk_box_pack_start(
202        GTK_BOX(uses_vbox),
203        gtk_util::IndentWidget(gtk_util::LeftAlignMisc(gtk_label_new(
204            usages[i].c_str()))),
205        FALSE, FALSE, 0);
206
207  gtk_box_pack_start(GTK_BOX(general_page_vbox_), gtk_hseparator_new(),
208                     FALSE, FALSE, 0);
209
210  const int num_rows = 21;
211  GtkTable* table = GTK_TABLE(gtk_table_new(num_rows, 2, FALSE));
212  gtk_table_set_col_spacing(table, 0, gtk_util::kLabelSpacing);
213  gtk_table_set_row_spacings(table, gtk_util::kControlSpacing);
214
215  gtk_box_pack_start(GTK_BOX(general_page_vbox_), GTK_WIDGET(table),
216                     FALSE, FALSE, 0);
217  int row = 0;
218  const std::string alternative_text =
219      l10n_util::GetStringUTF8(IDS_CERT_INFO_FIELD_NOT_PRESENT);
220  AddTitle(table, row++,
221           l10n_util::GetStringUTF8(IDS_CERT_INFO_SUBJECT_GROUP));
222  AddKeyValue(table, row++,
223              l10n_util::GetStringUTF8(IDS_CERT_INFO_COMMON_NAME_LABEL),
224              x509_certificate_model::ProcessIDN(
225                  x509_certificate_model::GetSubjectCommonName(
226                      cert, alternative_text)));
227  AddKeyValue(table, row++,
228              l10n_util::GetStringUTF8(IDS_CERT_INFO_ORGANIZATION_LABEL),
229              x509_certificate_model::GetSubjectOrgName(
230                  cert, alternative_text));
231  AddKeyValue(table, row++,
232              l10n_util::GetStringUTF8(IDS_CERT_INFO_ORGANIZATIONAL_UNIT_LABEL),
233              x509_certificate_model::GetSubjectOrgUnitName(
234                  cert, alternative_text));
235  AddKeyValue(table, row++,
236              l10n_util::GetStringUTF8(IDS_CERT_INFO_SERIAL_NUMBER_LABEL),
237              x509_certificate_model::GetSerialNumberHexified(
238                  cert, alternative_text));
239
240  row += 2;  // Add spacing (kControlSpacing * 3 == kContentAreaSpacing).
241
242  AddTitle(table, row++,
243           l10n_util::GetStringUTF8(IDS_CERT_INFO_ISSUER_GROUP));
244  AddKeyValue(table, row++,
245              l10n_util::GetStringUTF8(IDS_CERT_INFO_COMMON_NAME_LABEL),
246              x509_certificate_model::ProcessIDN(
247                  x509_certificate_model::GetIssuerCommonName(
248                      cert, alternative_text)));
249  AddKeyValue(table, row++,
250              l10n_util::GetStringUTF8(IDS_CERT_INFO_ORGANIZATION_LABEL),
251              x509_certificate_model::GetIssuerOrgName(
252                  cert, alternative_text));
253  AddKeyValue(table, row++,
254              l10n_util::GetStringUTF8(IDS_CERT_INFO_ORGANIZATIONAL_UNIT_LABEL),
255              x509_certificate_model::GetIssuerOrgUnitName(
256                  cert, alternative_text));
257
258  row += 2;  // Add spacing (kControlSpacing * 3 == kContentAreaSpacing).
259
260  base::Time issued, expires;
261  std::string issued_str, expires_str;
262  if (x509_certificate_model::GetTimes(cert, &issued, &expires)) {
263    issued_str = UTF16ToUTF8(
264        base::TimeFormatShortDateNumeric(issued));
265    expires_str = UTF16ToUTF8(
266        base::TimeFormatShortDateNumeric(expires));
267  } else {
268    issued_str = alternative_text;
269    expires_str = alternative_text;
270  }
271  AddTitle(table, row++,
272           l10n_util::GetStringUTF8(IDS_CERT_INFO_VALIDITY_GROUP));
273  AddKeyValue(table, row++,
274              l10n_util::GetStringUTF8(IDS_CERT_INFO_ISSUED_ON_LABEL),
275              issued_str);
276  AddKeyValue(table, row++,
277              l10n_util::GetStringUTF8(IDS_CERT_INFO_EXPIRES_ON_LABEL),
278              expires_str);
279
280  row += 2;  // Add spacing (kControlSpacing * 3 == kContentAreaSpacing).
281
282  AddTitle(table, row++,
283           l10n_util::GetStringUTF8(IDS_CERT_INFO_FINGERPRINTS_GROUP));
284  AddKeyValue(table, row++,
285              l10n_util::GetStringUTF8(IDS_CERT_INFO_SHA256_FINGERPRINT_LABEL),
286              x509_certificate_model::HashCertSHA256(cert));
287  AddKeyValue(table, row++,
288              l10n_util::GetStringUTF8(IDS_CERT_INFO_SHA1_FINGERPRINT_LABEL),
289              x509_certificate_model::HashCertSHA1(cert));
290
291  DCHECK_EQ(row, num_rows);
292}
293
294void CertificateViewer::FillHierarchyStore(GtkTreeStore* hierarchy_store,
295                                           GtkTreeIter* leaf) const {
296  GtkTreeIter parent;
297  GtkTreeIter* parent_ptr = NULL;
298  GtkTreeIter iter;
299  gint index = cert_chain_list_.size() - 1;
300  for (net::X509Certificate::OSCertHandles::const_reverse_iterator i =
301          cert_chain_list_.rbegin();
302       i != cert_chain_list_.rend(); ++i, --index) {
303    gtk_tree_store_append(hierarchy_store, &iter, parent_ptr);
304    GtkTreeStore* fields_store = CreateFieldsTreeStore(*i);
305    gtk_tree_store_set(
306        hierarchy_store, &iter,
307        HIERARCHY_NAME, x509_certificate_model::GetTitle(*i).c_str(),
308        HIERARCHY_OBJECT, fields_store,
309        HIERARCHY_INDEX, index,
310        -1);
311    g_object_unref(fields_store);
312    parent = iter;
313    parent_ptr = &parent;
314  }
315  *leaf = iter;
316}
317
318// static
319void CertificateViewer::FillTreeStoreWithCertFields(
320    GtkTreeStore* store, net::X509Certificate::OSCertHandle cert) {
321  GtkTreeIter top;
322  gtk_tree_store_append(store, &top, NULL);
323  gtk_tree_store_set(
324      store, &top,
325      FIELDS_NAME, x509_certificate_model::GetTitle(cert).c_str(),
326      FIELDS_VALUE, "",
327      -1);
328
329  GtkTreeIter cert_iter;
330  gtk_tree_store_append(store, &cert_iter, &top);
331  gtk_tree_store_set(
332      store, &cert_iter,
333      FIELDS_NAME,
334      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_CERTIFICATE).c_str(),
335      FIELDS_VALUE, "",
336      -1);
337
338  std::string version_str;
339  std::string version = x509_certificate_model::GetVersion(cert);
340  if (!version.empty())
341    version_str = l10n_util::GetStringFUTF8(IDS_CERT_DETAILS_VERSION_FORMAT,
342                                            UTF8ToUTF16(version));
343  GtkTreeIter iter;
344  gtk_tree_store_append(store, &iter, &cert_iter);
345  gtk_tree_store_set(
346      store, &iter,
347      FIELDS_NAME,
348      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_VERSION).c_str(),
349      FIELDS_VALUE, version_str.c_str(),
350      -1);
351
352  gtk_tree_store_append(store, &iter, &cert_iter);
353  gtk_tree_store_set(
354      store, &iter,
355      FIELDS_NAME,
356      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_SERIAL_NUMBER).c_str(),
357      FIELDS_VALUE,
358      x509_certificate_model::GetSerialNumberHexified(
359          cert,
360          l10n_util::GetStringUTF8(IDS_CERT_INFO_FIELD_NOT_PRESENT)).c_str(),
361      -1);
362
363  gtk_tree_store_append(store, &iter, &cert_iter);
364  gtk_tree_store_set(
365      store, &iter,
366      FIELDS_NAME,
367      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_CERTIFICATE_SIG_ALG).c_str(),
368      FIELDS_VALUE,
369      x509_certificate_model::ProcessSecAlgorithmSignature(cert).c_str(),
370      -1);
371
372  gtk_tree_store_append(store, &iter, &cert_iter);
373  gtk_tree_store_set(
374      store, &iter,
375      FIELDS_NAME,
376      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_ISSUER).c_str(),
377      FIELDS_VALUE, x509_certificate_model::GetIssuerName(cert).c_str(),
378      -1);
379
380  GtkTreeIter validity_iter;
381  gtk_tree_store_append(store, &validity_iter, &cert_iter);
382  gtk_tree_store_set(
383      store, &validity_iter,
384      FIELDS_NAME,
385      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_VALIDITY).c_str(),
386      FIELDS_VALUE, "",
387      -1);
388
389  base::Time issued, expires;
390  std::string issued_str, expires_str;
391  if (x509_certificate_model::GetTimes(cert, &issued, &expires)) {
392    issued_str = UTF16ToUTF8(base::TimeFormatShortDateAndTime(issued));
393    expires_str = UTF16ToUTF8(base::TimeFormatShortDateAndTime(expires));
394  }
395  gtk_tree_store_append(store, &iter, &validity_iter);
396  gtk_tree_store_set(
397      store, &iter,
398      FIELDS_NAME,
399      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_NOT_BEFORE).c_str(),
400      FIELDS_VALUE, issued_str.c_str(),
401      -1);
402  gtk_tree_store_append(store, &iter, &validity_iter);
403  gtk_tree_store_set(
404      store, &iter,
405      FIELDS_NAME,
406      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_NOT_AFTER).c_str(),
407      FIELDS_VALUE, expires_str.c_str(),
408      -1);
409
410  gtk_tree_store_append(store, &iter, &cert_iter);
411  gtk_tree_store_set(
412      store, &iter,
413      FIELDS_NAME,
414      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_SUBJECT).c_str(),
415      FIELDS_VALUE, x509_certificate_model::GetSubjectName(cert).c_str(),
416      -1);
417
418  GtkTreeIter subject_public_key_iter;
419  gtk_tree_store_append(store, &subject_public_key_iter, &cert_iter);
420  gtk_tree_store_set(
421      store, &subject_public_key_iter,
422      FIELDS_NAME,
423      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_SUBJECT_KEY_INFO).c_str(),
424      FIELDS_VALUE, "",
425      -1);
426
427  gtk_tree_store_append(store, &iter, &subject_public_key_iter);
428  gtk_tree_store_set(
429      store, &iter,
430      FIELDS_NAME,
431      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_SUBJECT_KEY_ALG).c_str(),
432      FIELDS_VALUE,
433      x509_certificate_model::ProcessSecAlgorithmSubjectPublicKey(cert).c_str(),
434      -1);
435
436  gtk_tree_store_append(store, &iter, &subject_public_key_iter);
437  gtk_tree_store_set(
438      store, &iter,
439      FIELDS_NAME,
440      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_SUBJECT_KEY).c_str(),
441      FIELDS_VALUE,
442      x509_certificate_model::ProcessSubjectPublicKeyInfo(cert).c_str(),
443      -1);
444
445  x509_certificate_model::Extensions extensions;
446  x509_certificate_model::GetExtensions(
447      l10n_util::GetStringUTF8(IDS_CERT_EXTENSION_CRITICAL),
448      l10n_util::GetStringUTF8(IDS_CERT_EXTENSION_NON_CRITICAL),
449      cert, &extensions);
450
451  if (!extensions.empty()) {
452    GtkTreeIter extensions_iter;
453    gtk_tree_store_append(store, &extensions_iter, &cert_iter);
454    gtk_tree_store_set(
455        store, &extensions_iter,
456        FIELDS_NAME,
457        l10n_util::GetStringUTF8(IDS_CERT_DETAILS_EXTENSIONS).c_str(),
458        FIELDS_VALUE, "",
459        -1);
460
461    for (x509_certificate_model::Extensions::const_iterator i =
462         extensions.begin(); i != extensions.end(); ++i) {
463      gtk_tree_store_append(store, &iter, &extensions_iter);
464      gtk_tree_store_set(
465          store, &iter,
466          FIELDS_NAME, i->name.c_str(),
467          FIELDS_VALUE, i->value.c_str(),
468          -1);
469    }
470  }
471
472  gtk_tree_store_append(store, &iter, &top);
473  gtk_tree_store_set(
474      store, &iter,
475      FIELDS_NAME,
476      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_CERTIFICATE_SIG_ALG).c_str(),
477      FIELDS_VALUE,
478      x509_certificate_model::ProcessSecAlgorithmSignatureWrap(cert).c_str(),
479      -1);
480
481  gtk_tree_store_append(store, &iter, &top);
482  gtk_tree_store_set(
483      store, &iter,
484      FIELDS_NAME,
485      l10n_util::GetStringUTF8(IDS_CERT_DETAILS_CERTIFICATE_SIG_VALUE).c_str(),
486      FIELDS_VALUE,
487      x509_certificate_model::ProcessRawBitsSignatureWrap(cert).c_str(),
488      -1);
489}
490
491// static
492GtkTreeStore* CertificateViewer::CreateFieldsTreeStore(
493    net::X509Certificate::OSCertHandle cert) {
494  GtkTreeStore* fields_store = gtk_tree_store_new(FIELDS_COLUMNS, G_TYPE_STRING,
495                                                  G_TYPE_STRING);
496  FillTreeStoreWithCertFields(fields_store, cert);
497  return fields_store;
498}
499
500void CertificateViewer::InitDetailsPage() {
501  details_page_vbox_ = gtk_vbox_new(FALSE, gtk_util::kContentAreaSpacing);
502  gtk_container_set_border_width(GTK_CONTAINER(details_page_vbox_),
503                                 gtk_util::kContentAreaBorder);
504
505  GtkWidget* hierarchy_vbox = gtk_vbox_new(FALSE, gtk_util::kControlSpacing);
506  gtk_box_pack_start(GTK_BOX(details_page_vbox_), hierarchy_vbox,
507                     FALSE, FALSE, 0);
508
509  gtk_box_pack_start(GTK_BOX(hierarchy_vbox),
510                     gtk_util::CreateBoldLabel(l10n_util::GetStringUTF8(
511                         IDS_CERT_DETAILS_CERTIFICATE_HIERARCHY_LABEL)),
512                     FALSE, FALSE, 0);
513
514  GtkTreeStore* hierarchy_store = gtk_tree_store_new(HIERARCHY_COLUMNS,
515                                                     G_TYPE_STRING,
516                                                     G_TYPE_OBJECT,
517                                                     G_TYPE_INT);
518  GtkTreeIter hierarchy_leaf_iter;
519  FillHierarchyStore(hierarchy_store, &hierarchy_leaf_iter);
520  GtkWidget* hierarchy_tree = gtk_tree_view_new_with_model(
521      GTK_TREE_MODEL(hierarchy_store));
522  gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(hierarchy_tree), FALSE);
523  gtk_tree_view_append_column(
524      GTK_TREE_VIEW(hierarchy_tree),
525      gtk_tree_view_column_new_with_attributes("", gtk_cell_renderer_text_new(),
526                                               "text", HIERARCHY_NAME,
527                                               NULL));
528  gtk_tree_view_expand_all(GTK_TREE_VIEW(hierarchy_tree));
529  hierarchy_selection_ = gtk_tree_view_get_selection(
530      GTK_TREE_VIEW(hierarchy_tree));
531  gtk_tree_selection_set_mode(hierarchy_selection_, GTK_SELECTION_SINGLE);
532  g_signal_connect(hierarchy_selection_, "changed",
533                   G_CALLBACK(OnHierarchySelectionChanged), this);
534  GtkWidget* hierarchy_scroll_window = gtk_scrolled_window_new(NULL, NULL);
535  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(hierarchy_scroll_window),
536                                 GTK_POLICY_AUTOMATIC,
537                                 GTK_POLICY_NEVER);
538  gtk_scrolled_window_set_shadow_type(
539      GTK_SCROLLED_WINDOW(hierarchy_scroll_window), GTK_SHADOW_ETCHED_IN);
540  gtk_container_add(GTK_CONTAINER(hierarchy_scroll_window), hierarchy_tree);
541  gtk_box_pack_start(GTK_BOX(hierarchy_vbox),
542                     hierarchy_scroll_window, FALSE, FALSE, 0);
543
544  GtkWidget* fields_vbox = gtk_vbox_new(FALSE, gtk_util::kControlSpacing);
545  gtk_box_pack_start(GTK_BOX(details_page_vbox_), fields_vbox,
546                     TRUE, TRUE, 0);
547  gtk_box_pack_start(GTK_BOX(fields_vbox),
548                     gtk_util::CreateBoldLabel(l10n_util::GetStringUTF8(
549                         IDS_CERT_DETAILS_CERTIFICATE_FIELDS_LABEL)),
550                     FALSE, FALSE, 0);
551
552  fields_tree_ = gtk_tree_view_new();
553  gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(fields_tree_), FALSE);
554  gtk_tree_view_append_column(
555      GTK_TREE_VIEW(fields_tree_),
556      gtk_tree_view_column_new_with_attributes("", gtk_cell_renderer_text_new(),
557                                               "text", FIELDS_NAME,
558                                               NULL));
559  GtkTreeSelection* fields_selection = gtk_tree_view_get_selection(
560      GTK_TREE_VIEW(fields_tree_));
561  gtk_tree_selection_set_mode(fields_selection, GTK_SELECTION_SINGLE);
562  g_signal_connect(fields_selection, "changed",
563                   G_CALLBACK(OnFieldsSelectionChanged), this);
564  GtkWidget* fields_scroll_window = gtk_scrolled_window_new(NULL, NULL);
565  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(fields_scroll_window),
566                                 GTK_POLICY_AUTOMATIC,
567                                 GTK_POLICY_AUTOMATIC);
568  gtk_scrolled_window_set_shadow_type(
569      GTK_SCROLLED_WINDOW(fields_scroll_window), GTK_SHADOW_ETCHED_IN);
570  gtk_container_add(GTK_CONTAINER(fields_scroll_window), fields_tree_);
571  gtk_box_pack_start(GTK_BOX(fields_vbox),
572                     fields_scroll_window, TRUE, TRUE, 0);
573
574  GtkWidget* value_vbox = gtk_vbox_new(FALSE, gtk_util::kControlSpacing);
575  gtk_box_pack_start(GTK_BOX(details_page_vbox_), value_vbox,
576                     TRUE, TRUE, 0);
577  gtk_box_pack_start(GTK_BOX(value_vbox),
578                     gtk_util::CreateBoldLabel(l10n_util::GetStringUTF8(
579                         IDS_CERT_DETAILS_CERTIFICATE_FIELD_VALUE_LABEL)),
580                     FALSE, FALSE, 0);
581
582  // TODO(mattm): fix text view coloring (should have grey background).
583  GtkWidget* field_value_view = gtk_text_view_new();
584  gtk_text_view_set_editable(GTK_TEXT_VIEW(field_value_view), FALSE);
585  gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(field_value_view), GTK_WRAP_NONE);
586  field_value_buffer_ = gtk_text_view_get_buffer(
587      GTK_TEXT_VIEW(field_value_view));
588  GtkWidget* value_scroll_window = gtk_scrolled_window_new(NULL, NULL);
589  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(value_scroll_window),
590                                 GTK_POLICY_AUTOMATIC,
591                                 GTK_POLICY_AUTOMATIC);
592  gtk_scrolled_window_set_shadow_type(
593      GTK_SCROLLED_WINDOW(value_scroll_window), GTK_SHADOW_ETCHED_IN);
594  gtk_container_add(GTK_CONTAINER(value_scroll_window), field_value_view);
595  gtk_box_pack_start(GTK_BOX(value_vbox),
596                     value_scroll_window, TRUE, TRUE, 0);
597
598  gtk_widget_ensure_style(field_value_view);
599  PangoFontDescription* font_desc = pango_font_description_copy(
600      gtk_widget_get_style(field_value_view)->font_desc);
601  pango_font_description_set_family(font_desc, kDetailsFontFamily);
602  gtk_widget_modify_font(field_value_view, font_desc);
603  pango_font_description_free(font_desc);
604
605  GtkWidget* export_hbox = gtk_hbox_new(FALSE, 0);
606  gtk_box_pack_start(GTK_BOX(details_page_vbox_), export_hbox,
607                     FALSE, FALSE, 0);
608  export_button_ = gtk_button_new_with_mnemonic(
609      gfx::ConvertAcceleratorsFromWindowsStyle(
610          l10n_util::GetStringUTF8(
611              IDS_CERT_DETAILS_EXPORT_CERTIFICATE)).c_str());
612  g_signal_connect(export_button_, "clicked",
613                   G_CALLBACK(OnExportClicked), this);
614  gtk_box_pack_start(GTK_BOX(export_hbox), export_button_,
615                     FALSE, FALSE, 0);
616
617  // Select the initial certificate in the hierarchy.
618  gtk_tree_selection_select_iter(hierarchy_selection_, &hierarchy_leaf_iter);
619}
620
621// static
622void CertificateViewer::OnHierarchySelectionChanged(
623    GtkTreeSelection* selection, CertificateViewer* viewer) {
624  GtkTreeIter iter;
625  GtkTreeModel* model;
626  if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
627    GtkTreeStore* fields_store = NULL;
628    gtk_tree_model_get(model, &iter, HIERARCHY_OBJECT, &fields_store, -1);
629    gtk_tree_view_set_model(GTK_TREE_VIEW(viewer->fields_tree_),
630                            GTK_TREE_MODEL(fields_store));
631    gtk_tree_view_expand_all(GTK_TREE_VIEW(viewer->fields_tree_));
632    gtk_widget_set_sensitive(viewer->export_button_, TRUE);
633  } else {
634    gtk_tree_view_set_model(GTK_TREE_VIEW(viewer->fields_tree_), NULL);
635    gtk_widget_set_sensitive(viewer->export_button_, FALSE);
636  }
637}
638
639// static
640void CertificateViewer::OnFieldsSelectionChanged(GtkTreeSelection* selection,
641                                                 CertificateViewer* viewer) {
642  GtkTreeIter iter;
643  GtkTreeModel* model;
644  if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
645    gchar* value_string = NULL;
646    gtk_tree_model_get(model, &iter, FIELDS_VALUE, &value_string, -1);
647    if (value_string) {
648      gtk_text_buffer_set_text(viewer->field_value_buffer_, value_string, -1);
649      g_free(value_string);
650    } else {
651      gtk_text_buffer_set_text(viewer->field_value_buffer_, "", 0);
652    }
653  } else {
654    gtk_text_buffer_set_text(viewer->field_value_buffer_, "", 0);
655  }
656}
657
658// static
659void CertificateViewer::OnExportClicked(GtkButton *button,
660                                        CertificateViewer* viewer) {
661  GtkTreeIter iter;
662  GtkTreeModel* model;
663  if (!gtk_tree_selection_get_selected(viewer->hierarchy_selection_, &model,
664                                       &iter))
665    return;
666  gint cert_index = -1;
667  gtk_tree_model_get(model, &iter, HIERARCHY_INDEX, &cert_index, -1);
668
669  if (cert_index < 0) {
670    NOTREACHED();
671    return;
672  }
673
674  ShowCertExportDialog(GTK_WINDOW(viewer->dialog_),
675                       viewer->cert_chain_list_[cert_index]);
676}
677
678void CertificateViewer::Show() {
679  gtk_util::ShowDialog(dialog_);
680}
681
682} // namespace
683
684void ShowCertificateViewer(gfx::NativeWindow parent,
685                           net::X509Certificate::OSCertHandle cert) {
686  net::X509Certificate::OSCertHandles cert_chain;
687  x509_certificate_model::GetCertChainFromCert(cert, &cert_chain);
688  (new CertificateViewer(parent, cert_chain))->Show();
689}
690
691void ShowCertificateViewer(gfx::NativeWindow parent,
692                           net::X509Certificate* cert) {
693  ShowCertificateViewer(parent, cert->os_cert_handle());
694}
695