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/memory/scoped_ptr.h"
14#include "base/string_number_conversions.h"
15#include "base/time.h"
16#include "base/utf_string_conversions.h"
17#include "chrome/browser/ui/gtk/certificate_dialogs.h"
18#include "chrome/browser/ui/gtk/gtk_util.h"
19#include "chrome/common/net/x509_certificate_model.h"
20#include "grit/generated_resources.h"
21#include "net/base/x509_certificate.h"
22#include "ui/base/l10n/l10n_util.h"
23#include "ui/gfx/gtk_util.h"
24
25namespace {
26
27const char kDetailsFontFamily[] = "monospace";
28
29////////////////////////////////////////////////////////////////////////////////
30// Gtk utility functions.
31
32void AddTitle(GtkTable* table, int row, const std::string& text) {
33  gtk_table_attach_defaults(table,
34                            gtk_util::CreateBoldLabel(text),
35                            0, 2,
36                            row, row + 1);
37}
38
39void AddKeyValue(GtkTable* table, int row, const std::string& text,
40                 const std::string& value) {
41  gtk_table_attach_defaults(
42      table,
43      gtk_util::IndentWidget(
44          gtk_util::LeftAlignMisc(gtk_label_new(text.c_str()))),
45      0, 1, row, row + 1);
46  GtkWidget* label = gtk_label_new(value.c_str());
47  gtk_label_set_selectable(GTK_LABEL(label), TRUE);
48  gtk_table_attach_defaults(
49      table,
50      gtk_util::LeftAlignMisc(label),
51      1, 2, row, row + 1);
52}
53
54////////////////////////////////////////////////////////////////////////////////
55// CertificateViewer class definition.
56
57class CertificateViewer {
58 public:
59  CertificateViewer(gfx::NativeWindow parent,
60                    const net::X509Certificate::OSCertHandles& cert_chain_list);
61  ~CertificateViewer();
62
63  void InitGeneralPage();
64  void InitDetailsPage();
65
66  void Show();
67
68 private:
69  // Indices and column count for the certificate chain hierarchy tree store.
70  enum {
71    HIERARCHY_NAME,
72    HIERARCHY_OBJECT,
73    HIERARCHY_INDEX,
74    HIERARCHY_COLUMNS
75  };
76
77  // Indices and column count for the certificate fields tree store.
78  enum {
79    FIELDS_NAME,
80    FIELDS_VALUE,
81    FIELDS_COLUMNS
82  };
83
84  // Fill the tree store with the certificate hierarchy, and set |leaf| to the
85  // iter of the leaf node.
86  void FillHierarchyStore(GtkTreeStore* hierarchy_store,
87                          GtkTreeIter* leaf) const;
88
89  // Fill the tree store with the details of the given certificate.
90  static void FillTreeStoreWithCertFields(
91      GtkTreeStore* store, net::X509Certificate::OSCertHandle cert);
92
93  // Create a tree store filled with the details of the given certificate.
94  static GtkTreeStore* CreateFieldsTreeStore(
95      net::X509Certificate::OSCertHandle cert);
96
97  // Callbacks for user selecting elements in the trees.
98  static void OnHierarchySelectionChanged(GtkTreeSelection* selection,
99                                          CertificateViewer* viewer);
100  static void OnFieldsSelectionChanged(GtkTreeSelection* selection,
101                                       CertificateViewer* viewer);
102
103  // Callback for export button.
104  static void OnExportClicked(GtkButton *button, CertificateViewer* viewer);
105
106  // The certificate hierarchy (leaf cert first).
107  net::X509Certificate::OSCertHandles cert_chain_list_;
108
109  GtkWidget* dialog_;
110  GtkWidget* notebook_;
111  GtkWidget* general_page_vbox_;
112  GtkWidget* details_page_vbox_;
113  GtkTreeSelection* hierarchy_selection_;
114  GtkWidget* fields_tree_;
115  GtkTextBuffer* field_value_buffer_;
116  GtkWidget* export_button_;
117
118  DISALLOW_COPY_AND_ASSIGN(CertificateViewer);
119};
120
121////////////////////////////////////////////////////////////////////////////////
122// CertificateViewer implementation.
123
124// Close button callback.
125void OnResponse(GtkWidget* dialog, int response_id) {
126  // "Close" was clicked.
127  gtk_widget_destroy(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(OnResponse), 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  GtkTreeIter top_fingerprints_iter;
491  gtk_tree_store_append(store, &top_fingerprints_iter, &top);
492  gtk_tree_store_set(
493      store, &top_fingerprints_iter,
494      FIELDS_NAME,
495      l10n_util::GetStringUTF8(IDS_CERT_INFO_FINGERPRINTS_GROUP).c_str(),
496      FIELDS_VALUE, "",
497      -1);
498
499  GtkTreeIter fingerprints_iter;
500  gtk_tree_store_append(store, &fingerprints_iter, &top_fingerprints_iter);
501  gtk_tree_store_set(
502      store, &fingerprints_iter,
503      FIELDS_NAME,
504      l10n_util::GetStringUTF8(IDS_CERT_INFO_SHA256_FINGERPRINT_LABEL).c_str(),
505      FIELDS_VALUE, x509_certificate_model::HashCertSHA256(cert).c_str(),
506      -1);
507
508  gtk_tree_store_append(store, &fingerprints_iter, &top_fingerprints_iter);
509  gtk_tree_store_set(
510      store, &fingerprints_iter,
511      FIELDS_NAME,
512      l10n_util::GetStringUTF8(IDS_CERT_INFO_SHA1_FINGERPRINT_LABEL).c_str(),
513      FIELDS_VALUE, x509_certificate_model::HashCertSHA1(cert).c_str(),
514      -1);
515}
516
517// static
518GtkTreeStore* CertificateViewer::CreateFieldsTreeStore(
519    net::X509Certificate::OSCertHandle cert) {
520  GtkTreeStore* fields_store = gtk_tree_store_new(FIELDS_COLUMNS, G_TYPE_STRING,
521                                                  G_TYPE_STRING);
522  FillTreeStoreWithCertFields(fields_store, cert);
523  return fields_store;
524}
525
526void CertificateViewer::InitDetailsPage() {
527  details_page_vbox_ = gtk_vbox_new(FALSE, gtk_util::kContentAreaSpacing);
528  gtk_container_set_border_width(GTK_CONTAINER(details_page_vbox_),
529                                 gtk_util::kContentAreaBorder);
530
531  GtkWidget* hierarchy_vbox = gtk_vbox_new(FALSE, gtk_util::kControlSpacing);
532  gtk_box_pack_start(GTK_BOX(details_page_vbox_), hierarchy_vbox,
533                     FALSE, FALSE, 0);
534
535  gtk_box_pack_start(GTK_BOX(hierarchy_vbox),
536                     gtk_util::CreateBoldLabel(l10n_util::GetStringUTF8(
537                         IDS_CERT_DETAILS_CERTIFICATE_HIERARCHY_LABEL)),
538                     FALSE, FALSE, 0);
539
540  GtkTreeStore* hierarchy_store = gtk_tree_store_new(HIERARCHY_COLUMNS,
541                                                     G_TYPE_STRING,
542                                                     G_TYPE_OBJECT,
543                                                     G_TYPE_INT);
544  GtkTreeIter hierarchy_leaf_iter;
545  FillHierarchyStore(hierarchy_store, &hierarchy_leaf_iter);
546  GtkWidget* hierarchy_tree = gtk_tree_view_new_with_model(
547      GTK_TREE_MODEL(hierarchy_store));
548  gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(hierarchy_tree), FALSE);
549  gtk_tree_view_append_column(
550      GTK_TREE_VIEW(hierarchy_tree),
551      gtk_tree_view_column_new_with_attributes("", gtk_cell_renderer_text_new(),
552                                               "text", HIERARCHY_NAME,
553                                               NULL));
554  gtk_tree_view_expand_all(GTK_TREE_VIEW(hierarchy_tree));
555  hierarchy_selection_ = gtk_tree_view_get_selection(
556      GTK_TREE_VIEW(hierarchy_tree));
557  gtk_tree_selection_set_mode(hierarchy_selection_, GTK_SELECTION_SINGLE);
558  g_signal_connect(hierarchy_selection_, "changed",
559                   G_CALLBACK(OnHierarchySelectionChanged), this);
560  GtkWidget* hierarchy_scroll_window = gtk_scrolled_window_new(NULL, NULL);
561  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(hierarchy_scroll_window),
562                                 GTK_POLICY_AUTOMATIC,
563                                 GTK_POLICY_NEVER);
564  gtk_scrolled_window_set_shadow_type(
565      GTK_SCROLLED_WINDOW(hierarchy_scroll_window), GTK_SHADOW_ETCHED_IN);
566  gtk_container_add(GTK_CONTAINER(hierarchy_scroll_window), hierarchy_tree);
567  gtk_box_pack_start(GTK_BOX(hierarchy_vbox),
568                     hierarchy_scroll_window, FALSE, FALSE, 0);
569
570  GtkWidget* fields_vbox = gtk_vbox_new(FALSE, gtk_util::kControlSpacing);
571  gtk_box_pack_start(GTK_BOX(details_page_vbox_), fields_vbox,
572                     TRUE, TRUE, 0);
573  gtk_box_pack_start(GTK_BOX(fields_vbox),
574                     gtk_util::CreateBoldLabel(l10n_util::GetStringUTF8(
575                         IDS_CERT_DETAILS_CERTIFICATE_FIELDS_LABEL)),
576                     FALSE, FALSE, 0);
577
578  fields_tree_ = gtk_tree_view_new();
579  gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(fields_tree_), FALSE);
580  gtk_tree_view_append_column(
581      GTK_TREE_VIEW(fields_tree_),
582      gtk_tree_view_column_new_with_attributes("", gtk_cell_renderer_text_new(),
583                                               "text", FIELDS_NAME,
584                                               NULL));
585  GtkTreeSelection* fields_selection = gtk_tree_view_get_selection(
586      GTK_TREE_VIEW(fields_tree_));
587  gtk_tree_selection_set_mode(fields_selection, GTK_SELECTION_SINGLE);
588  g_signal_connect(fields_selection, "changed",
589                   G_CALLBACK(OnFieldsSelectionChanged), this);
590  GtkWidget* fields_scroll_window = gtk_scrolled_window_new(NULL, NULL);
591  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(fields_scroll_window),
592                                 GTK_POLICY_AUTOMATIC,
593                                 GTK_POLICY_AUTOMATIC);
594  gtk_scrolled_window_set_shadow_type(
595      GTK_SCROLLED_WINDOW(fields_scroll_window), GTK_SHADOW_ETCHED_IN);
596  gtk_container_add(GTK_CONTAINER(fields_scroll_window), fields_tree_);
597  gtk_box_pack_start(GTK_BOX(fields_vbox),
598                     fields_scroll_window, TRUE, TRUE, 0);
599
600  GtkWidget* value_vbox = gtk_vbox_new(FALSE, gtk_util::kControlSpacing);
601  gtk_box_pack_start(GTK_BOX(details_page_vbox_), value_vbox,
602                     TRUE, TRUE, 0);
603  gtk_box_pack_start(GTK_BOX(value_vbox),
604                     gtk_util::CreateBoldLabel(l10n_util::GetStringUTF8(
605                         IDS_CERT_DETAILS_CERTIFICATE_FIELD_VALUE_LABEL)),
606                     FALSE, FALSE, 0);
607
608  // TODO(mattm): fix text view coloring (should have grey background).
609  GtkWidget* field_value_view = gtk_text_view_new();
610  gtk_text_view_set_editable(GTK_TEXT_VIEW(field_value_view), FALSE);
611  gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(field_value_view), GTK_WRAP_NONE);
612  field_value_buffer_ = gtk_text_view_get_buffer(
613      GTK_TEXT_VIEW(field_value_view));
614  GtkWidget* value_scroll_window = gtk_scrolled_window_new(NULL, NULL);
615  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(value_scroll_window),
616                                 GTK_POLICY_AUTOMATIC,
617                                 GTK_POLICY_AUTOMATIC);
618  gtk_scrolled_window_set_shadow_type(
619      GTK_SCROLLED_WINDOW(value_scroll_window), GTK_SHADOW_ETCHED_IN);
620  gtk_container_add(GTK_CONTAINER(value_scroll_window), field_value_view);
621  gtk_box_pack_start(GTK_BOX(value_vbox),
622                     value_scroll_window, TRUE, TRUE, 0);
623
624  gtk_widget_ensure_style(field_value_view);
625  PangoFontDescription* font_desc = pango_font_description_copy(
626      gtk_widget_get_style(field_value_view)->font_desc);
627  pango_font_description_set_family(font_desc, kDetailsFontFamily);
628  gtk_widget_modify_font(field_value_view, font_desc);
629  pango_font_description_free(font_desc);
630
631  GtkWidget* export_hbox = gtk_hbox_new(FALSE, 0);
632  gtk_box_pack_start(GTK_BOX(details_page_vbox_), export_hbox,
633                     FALSE, FALSE, 0);
634  export_button_ = gtk_button_new_with_mnemonic(
635      gfx::ConvertAcceleratorsFromWindowsStyle(
636          l10n_util::GetStringUTF8(
637              IDS_CERT_DETAILS_EXPORT_CERTIFICATE)).c_str());
638  g_signal_connect(export_button_, "clicked",
639                   G_CALLBACK(OnExportClicked), this);
640  gtk_box_pack_start(GTK_BOX(export_hbox), export_button_,
641                     FALSE, FALSE, 0);
642
643  // Select the initial certificate in the hierarchy.
644  gtk_tree_selection_select_iter(hierarchy_selection_, &hierarchy_leaf_iter);
645}
646
647// static
648void CertificateViewer::OnHierarchySelectionChanged(
649    GtkTreeSelection* selection, CertificateViewer* viewer) {
650  GtkTreeIter iter;
651  GtkTreeModel* model;
652  if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
653    GtkTreeStore* fields_store = NULL;
654    gtk_tree_model_get(model, &iter, HIERARCHY_OBJECT, &fields_store, -1);
655    gtk_tree_view_set_model(GTK_TREE_VIEW(viewer->fields_tree_),
656                            GTK_TREE_MODEL(fields_store));
657    gtk_tree_view_expand_all(GTK_TREE_VIEW(viewer->fields_tree_));
658    gtk_widget_set_sensitive(viewer->export_button_, TRUE);
659  } else {
660    gtk_tree_view_set_model(GTK_TREE_VIEW(viewer->fields_tree_), NULL);
661    gtk_widget_set_sensitive(viewer->export_button_, FALSE);
662  }
663}
664
665// static
666void CertificateViewer::OnFieldsSelectionChanged(GtkTreeSelection* selection,
667                                                 CertificateViewer* viewer) {
668  GtkTreeIter iter;
669  GtkTreeModel* model;
670  if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
671    gchar* value_string = NULL;
672    gtk_tree_model_get(model, &iter, FIELDS_VALUE, &value_string, -1);
673    if (value_string) {
674      gtk_text_buffer_set_text(viewer->field_value_buffer_, value_string, -1);
675      g_free(value_string);
676    } else {
677      gtk_text_buffer_set_text(viewer->field_value_buffer_, "", 0);
678    }
679  } else {
680    gtk_text_buffer_set_text(viewer->field_value_buffer_, "", 0);
681  }
682}
683
684// static
685void CertificateViewer::OnExportClicked(GtkButton *button,
686                                        CertificateViewer* viewer) {
687  GtkTreeIter iter;
688  GtkTreeModel* model;
689  if (!gtk_tree_selection_get_selected(viewer->hierarchy_selection_, &model,
690                                       &iter))
691    return;
692  gint cert_index = -1;
693  gtk_tree_model_get(model, &iter, HIERARCHY_INDEX, &cert_index, -1);
694
695  if (cert_index < 0) {
696    NOTREACHED();
697    return;
698  }
699
700  ShowCertExportDialog(NULL, GTK_WINDOW(viewer->dialog_),
701                       viewer->cert_chain_list_[cert_index]);
702}
703
704void CertificateViewer::Show() {
705  gtk_util::ShowDialog(dialog_);
706}
707
708} // namespace
709
710void ShowCertificateViewer(gfx::NativeWindow parent,
711                           net::X509Certificate::OSCertHandle cert) {
712  net::X509Certificate::OSCertHandles cert_chain;
713  x509_certificate_model::GetCertChainFromCert(cert, &cert_chain);
714  (new CertificateViewer(parent, cert_chain))->Show();
715}
716
717void ShowCertificateViewer(gfx::NativeWindow parent,
718                           net::X509Certificate* cert) {
719  ShowCertificateViewer(parent, cert->os_cert_handle());
720}
721