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