Home | History | Annotate | Download | only in resources
      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 cr.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 
    211 document.addEventListener('DOMContentLoaded', cert_viewer.initialize);
    212