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
5cr.define('cert_viewer', function() {
6  'use strict';
7
8  /**
9   * Initialize the certificate viewer dialog by wiring up the close button,
10   * substituting in translated strings and requesting certificate details.
11   */
12  function initialize() {
13    cr.ui.decorate('tabbox', cr.ui.TabBox);
14
15    var args = JSON.parse(chrome.getVariableValue('dialogArguments'));
16    getCertificateInfo(args);
17
18    /**
19     * Initialize the second tab's contents.
20     * This is a 'oneShot' function, meaning it will only be invoked once,
21     * no matter how many times it is called.  This is done for unit-testing
22     * purposes in case a test needs to initialize the tab before the timer
23     * fires.
24     */
25    var initializeDetailTab = oneShot(function() {
26      initializeTree($('hierarchy'), showCertificateFields);
27      initializeTree($('cert-fields'), showCertificateFieldValue);
28      createCertificateHierarchy(args.hierarchy);
29    });
30
31    // The second tab's contents aren't visible on startup, so we can
32    // shorten startup by not populating those controls until after we
33    // have had a chance to draw the visible controls the first time.
34    // The value of 200ms is quick enough that the user couldn't open the
35    // tab in that time but long enough to allow the first tab to draw on
36    // even the slowest machine.
37    setTimeout(initializeDetailTab, 200);
38
39    $('tabbox').addEventListener('selectedChange', function f(e) {
40      $('tabbox').removeEventListener('selectedChange', f);
41      initializeDetailTab();
42    }, true);
43
44    stripGtkAccessorKeys();
45
46    $('export').onclick = exportCertificate;
47  }
48
49  /**
50   * Decorate a function so that it can only be invoked once.
51   */
52  function oneShot(fn) {
53    var fired = false;
54    return function() {
55       if (fired) return;
56       fired = true;
57       fn();
58    };
59  }
60
61  /**
62   * Initialize a cr.ui.Tree object from a given element using the specified
63   * change handler.
64   * @param {HTMLElement} tree The HTMLElement to style as a tree.
65   * @param {function()} handler Function to call when a node is selected.
66   */
67  function initializeTree(tree, handler) {
68    cr.ui.decorate(tree, cr.ui.Tree);
69    tree.detail = {payload: {}, children: {}};
70    tree.addEventListener('change', handler);
71  }
72
73  /**
74   * The tab name strings in the languages file have accessor keys indicated
75   * by a preceding & sign. Strip these out for now.
76   * TODO(flackr) These accessor keys could be implemented with Javascript or
77   *     translated strings could be added / modified to remove the & sign.
78   */
79  function stripGtkAccessorKeys() {
80    // Copy all the tab labels into an array.
81    var nodes = Array.prototype.slice.call($('tabs').childNodes, 0);
82    nodes.push($('export'));
83    for (var i = 0; i < nodes.length; i++)
84      nodes[i].textContent = nodes[i].textContent.replace('&', '');
85  }
86
87  /**
88   * Expand all nodes of the given tree object.
89   * @param {cr.ui.Tree} tree The tree object to expand all nodes on.
90   */
91  function revealTree(tree) {
92    tree.expanded = true;
93    for (var key in tree.detail.children) {
94      revealTree(tree.detail.children[key]);
95    }
96  }
97
98  /**
99   * This function is called from certificate_viewer_ui.cc with the certificate
100   * information. Display all returned information to the user.
101   * @param {Object} certInfo Certificate information in named fields.
102   */
103  function getCertificateInfo(certInfo) {
104    for (var key in certInfo.general) {
105      $(key).textContent = certInfo.general[key];
106    }
107  }
108
109  /**
110   * This function populates the certificate hierarchy.
111   * @param {Object} hierarchy A dictionary containing the hierarchy.
112   */
113  function createCertificateHierarchy(hierarchy) {
114    var treeItem = $('hierarchy');
115    var root = constructTree(hierarchy[0]);
116    treeItem.detail.children['root'] = root;
117    treeItem.add(root);
118
119    // Select the last item in the hierarchy (really we have a list here - each
120    // node has at most one child).  This will reveal the parent nodes and
121    // populate the fields view.
122    var last = root;
123    while (last.detail.children && last.detail.children[0])
124      last = last.detail.children[0];
125    last.selected = true;
126  }
127
128  /**
129   * Constructs a cr.ui.TreeItem corresponding to the passed in tree
130   * @param {Object} tree Dictionary describing the tree structure.
131   * @return {cr.ui.TreeItem} Tree node corresponding to the input dictionary.
132   */
133  function constructTree(tree)
134  {
135    var treeItem = new cr.ui.TreeItem({
136        label: tree.label,
137        detail: {payload: tree.payload ? tree.payload : {},
138            children: {}
139        }});
140    if (tree.children) {
141      for (var i = 0; i < tree.children.length; i++) {
142        treeItem.add(treeItem.detail.children[i] =
143            constructTree(tree.children[i]));
144      }
145    }
146    return treeItem;
147  }
148
149  /**
150   * Clear any previous certificate fields in the tree.
151   */
152  function clearCertificateFields() {
153    var treeItem = $('cert-fields');
154    for (var key in treeItem.detail.children) {
155      treeItem.remove(treeItem.detail.children[key]);
156      delete treeItem.detail.children[key];
157    }
158  }
159
160  /**
161   * Request certificate fields for the selected certificate in the hierarchy.
162   */
163  function showCertificateFields() {
164    clearCertificateFields();
165    var item = $('hierarchy').selectedItem;
166    if (item && item.detail.payload.index !== undefined)
167      chrome.send('requestCertificateFields', [item.detail.payload.index]);
168  }
169
170  /**
171   * Show the returned certificate fields for the selected certificate.
172   * @param {Object} certFields A dictionary containing the fields tree
173   *     structure.
174   */
175  function getCertificateFields(certFields) {
176    clearCertificateFields();
177    var treeItem = $('cert-fields');
178    treeItem.add(treeItem.detail.children['root'] =
179        constructTree(certFields[0]));
180    revealTree(treeItem);
181    // Ensure the list is scrolled to the top by selecting the first item.
182    treeItem.children[0].selected = true;
183  }
184
185  /**
186   * Show certificate field value for a selected certificate field.
187   */
188  function showCertificateFieldValue() {
189    var item = $('cert-fields').selectedItem;
190    if (item && item.detail.payload.val)
191      $('cert-field-value').textContent = item.detail.payload.val;
192    else
193      $('cert-field-value').textContent = '';
194  }
195
196  /**
197   * Export the selected certificate.
198   */
199  function exportCertificate() {
200    var item = $('hierarchy').selectedItem;
201    if (item && item.detail.payload.index !== undefined)
202      chrome.send('exportCertificate', [item.detail.payload.index]);
203  }
204
205  return {
206    initialize: initialize,
207    getCertificateFields: getCertificateFields,
208  };
209});
210
211document.addEventListener('DOMContentLoaded', cert_viewer.initialize);
212