// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. cr.define('cert_viewer', function() { 'use strict'; /** * Initialize the certificate viewer dialog by wiring up the close button, * substituting in translated strings and requesting certificate details. */ function initialize() { cr.ui.decorate('tabbox', cr.ui.TabBox); var args = JSON.parse(chrome.getVariableValue('dialogArguments')); getCertificateInfo(args); /** * Initialize the second tab's contents. * This is a 'oneShot' function, meaning it will only be invoked once, * no matter how many times it is called. This is done for unit-testing * purposes in case a test needs to initialize the tab before the timer * fires. */ var initializeDetailTab = oneShot(function() { initializeTree($('hierarchy'), showCertificateFields); initializeTree($('cert-fields'), showCertificateFieldValue); createCertificateHierarchy(args.hierarchy); }); // The second tab's contents aren't visible on startup, so we can // shorten startup by not populating those controls until after we // have had a chance to draw the visible controls the first time. // The value of 200ms is quick enough that the user couldn't open the // tab in that time but long enough to allow the first tab to draw on // even the slowest machine. setTimeout(initializeDetailTab, 200); $('tabbox').addEventListener('selectedChange', function f(e) { $('tabbox').removeEventListener('selectedChange', f); initializeDetailTab(); }, true); stripGtkAccessorKeys(); $('export').onclick = exportCertificate; } /** * Decorate a function so that it can only be invoked once. */ function oneShot(fn) { var fired = false; return function() { if (fired) return; fired = true; fn(); }; } /** * Initialize a cr.ui.Tree object from a given element using the specified * change handler. * @param {HTMLElement} tree The HTMLElement to style as a tree. * @param {function()} handler Function to call when a node is selected. */ function initializeTree(tree, handler) { cr.ui.decorate(tree, cr.ui.Tree); tree.detail = {payload: {}, children: {}}; tree.addEventListener('change', handler); } /** * The tab name strings in the languages file have accessor keys indicated * by a preceding & sign. Strip these out for now. * TODO(flackr) These accessor keys could be implemented with Javascript or * translated strings could be added / modified to remove the & sign. */ function stripGtkAccessorKeys() { // Copy all the tab labels into an array. var nodes = Array.prototype.slice.call($('tabs').childNodes, 0); nodes.push($('export')); for (var i = 0; i < nodes.length; i++) nodes[i].textContent = nodes[i].textContent.replace('&', ''); } /** * Expand all nodes of the given tree object. * @param {cr.ui.Tree} tree The tree object to expand all nodes on. */ function revealTree(tree) { tree.expanded = true; for (var key in tree.detail.children) { revealTree(tree.detail.children[key]); } } /** * This function is called from certificate_viewer_ui.cc with the certificate * information. Display all returned information to the user. * @param {Object} certInfo Certificate information in named fields. */ function getCertificateInfo(certInfo) { for (var key in certInfo.general) { $(key).textContent = certInfo.general[key]; } } /** * This function populates the certificate hierarchy. * @param {Object} hierarchy A dictionary containing the hierarchy. */ function createCertificateHierarchy(hierarchy) { var treeItem = $('hierarchy'); var root = constructTree(hierarchy[0]); treeItem.detail.children['root'] = root; treeItem.add(root); // Select the last item in the hierarchy (really we have a list here - each // node has at most one child). This will reveal the parent nodes and // populate the fields view. var last = root; while (last.detail.children && last.detail.children[0]) last = last.detail.children[0]; last.selected = true; } /** * Constructs a cr.ui.TreeItem corresponding to the passed in tree * @param {Object} tree Dictionary describing the tree structure. * @return {cr.ui.TreeItem} Tree node corresponding to the input dictionary. */ function constructTree(tree) { var treeItem = new cr.ui.TreeItem({ label: tree.label, detail: {payload: tree.payload ? tree.payload : {}, children: {} }}); if (tree.children) { for (var i = 0; i < tree.children.length; i++) { treeItem.add(treeItem.detail.children[i] = constructTree(tree.children[i])); } } return treeItem; } /** * Clear any previous certificate fields in the tree. */ function clearCertificateFields() { var treeItem = $('cert-fields'); for (var key in treeItem.detail.children) { treeItem.remove(treeItem.detail.children[key]); delete treeItem.detail.children[key]; } } /** * Request certificate fields for the selected certificate in the hierarchy. */ function showCertificateFields() { clearCertificateFields(); var item = $('hierarchy').selectedItem; if (item && item.detail.payload.index !== undefined) chrome.send('requestCertificateFields', [item.detail.payload.index]); } /** * Show the returned certificate fields for the selected certificate. * @param {Object} certFields A dictionary containing the fields tree * structure. */ function getCertificateFields(certFields) { clearCertificateFields(); var treeItem = $('cert-fields'); treeItem.add(treeItem.detail.children['root'] = constructTree(certFields[0])); revealTree(treeItem); // Ensure the list is scrolled to the top by selecting the first item. treeItem.children[0].selected = true; } /** * Show certificate field value for a selected certificate field. */ function showCertificateFieldValue() { var item = $('cert-fields').selectedItem; if (item && item.detail.payload.val) $('cert-field-value').textContent = item.detail.payload.val; else $('cert-field-value').textContent = ''; } /** * Export the selected certificate. */ function exportCertificate() { var item = $('hierarchy').selectedItem; if (item && item.detail.payload.index !== undefined) chrome.send('exportCertificate', [item.detail.payload.index]); } return { initialize: initialize, getCertificateFields: getCertificateFields, }; }); document.addEventListener('DOMContentLoaded', cert_viewer.initialize);