Home | History | Annotate | Download | only in about_sys
      1 // Copyright 2013 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 // Contents of lines that act as delimiters for multi-line values.
      6 var DELIM_START = '---------- START ----------';
      7 var DELIM_END = '---------- END ----------';
      8 
      9 // Limit file size to 10 MiB to prevent hanging on accidental upload.
     10 var MAX_FILE_SIZE = 10485760;
     11 
     12 function getValueDivForButton(button) {
     13   return $(button.id.substr(0, button.id.length - 4));
     14 }
     15 
     16 function getButtonForValueDiv(valueDiv) {
     17   return $(valueDiv.id + '-btn');
     18 }
     19 
     20 function handleDragOver(e) {
     21   e.dataTransfer.dropEffect = 'copy';
     22   e.preventDefault();
     23 }
     24 
     25 function handleDrop(e) {
     26   var file = e.dataTransfer.files[0];
     27   if (file) {
     28     e.preventDefault();
     29     importLog(file);
     30   }
     31 }
     32 
     33 function showError(fileName) {
     34   $('status').textContent = loadTimeData.getStringF('parseError', fileName);
     35 }
     36 
     37 /**
     38  * Toggles whether an item is collapsed or expanded.
     39  */
     40 function changeCollapsedStatus() {
     41   var valueDiv = getValueDivForButton(this);
     42   if (valueDiv.parentNode.className == 'number-collapsed') {
     43     valueDiv.parentNode.className = 'number-expanded';
     44     this.textContent = loadTimeData.getString('collapseBtn');
     45   } else {
     46     valueDiv.parentNode.className = 'number-collapsed';
     47     this.textContent = loadTimeData.getString('expandBtn');
     48   }
     49 }
     50 
     51 /**
     52  * Collapses all log items.
     53  */
     54 function collapseAll() {
     55   var valueDivs = document.getElementsByClassName('stat-value');
     56   for (var i = 0; i < valueDivs.length; i++) {
     57     var button = getButtonForValueDiv(valueDivs[i]);
     58     if (button && button.className != 'button-hidden') {
     59       button.textContent = loadTimeData.getString('expandBtn');
     60       valueDivs[i].parentNode.className = 'number-collapsed';
     61     }
     62   }
     63 }
     64 
     65 /**
     66  * Expands all log items.
     67  */
     68 function expandAll() {
     69   var valueDivs = document.getElementsByClassName('stat-value');
     70   for (var i = 0; i < valueDivs.length; i++) {
     71     var button = getButtonForValueDiv(valueDivs[i]);
     72     if (button && button.className != 'button-hidden') {
     73       button.textContent = loadTimeData.getString('collapseBtn');
     74       valueDivs[i].parentNode.className = 'number-expanded';
     75     }
     76   }
     77 }
     78 
     79 /**
     80  * Collapse only those log items with multi-line values.
     81  */
     82 function collapseMultiLineStrings() {
     83   var valueDivs = document.getElementsByClassName('stat-value');
     84   var nameDivs = document.getElementsByClassName('stat-name');
     85   for (var i = 0; i < valueDivs.length; i++) {
     86     var button = getButtonForValueDiv(valueDivs[i]);
     87     button.onclick = changeCollapsedStatus;
     88     if (valueDivs[i].scrollHeight > (nameDivs[i].scrollHeight * 2)) {
     89       button.className = '';
     90       button.textContent = loadTimeData.getString('expandBtn');
     91       valueDivs[i].parentNode.className = 'number-collapsed';
     92     } else {
     93       button.className = 'button-hidden';
     94       valueDivs[i].parentNode.className = 'number';
     95     }
     96   }
     97 }
     98 
     99 /**
    100  * Read in a log asynchronously, calling parseSystemLog if successful.
    101  * @param {File} file The file to read.
    102  */
    103 function importLog(file) {
    104   if (file && file.size <= MAX_FILE_SIZE) {
    105     var reader = new FileReader();
    106     reader.onload = function() {
    107       if (parseSystemLog(this.result)) {
    108         // Reset table title and status
    109         $('tableTitle').textContent =
    110               loadTimeData.getStringF('logFileTableTitle', file.name);
    111         $('status').textContent = '';
    112       } else {
    113         showError(file.name);
    114       }
    115     };
    116     reader.readAsText(file);
    117   } else if (file) {
    118     showError(file.name);
    119   }
    120 }
    121 
    122 /**
    123  * Convert text-based log into list of name-value pairs.
    124  * @param {string} text The raw text of a log.
    125  * @return {boolean} True if the log was parsed successfully.
    126  */
    127 function parseSystemLog(text) {
    128   var details = [];
    129   var lines = text.split('\n');
    130   for (var i = 0, len = lines.length; i < len; i++) {
    131     // Skip empty lines.
    132     if (!lines[i])
    133       continue;
    134 
    135     var delimiter = lines[i].indexOf('=');
    136     if (delimiter <= 0) {
    137       if (i == lines.length - 1)
    138          break;
    139       // If '=' is missing here, format is wrong.
    140       return false;
    141     }
    142 
    143     var name = lines[i].substring(0, delimiter);
    144     var value = '';
    145     // Set value if non-empty
    146     if (lines[i].length > delimiter + 1)
    147       value = lines[i].substring(delimiter + 1);
    148 
    149     // Delimiters are based on kMultilineIndicatorString, kMultilineStartString,
    150     // and kMultilineEndString in components/feedback/feedback_data.cc.
    151     // If these change, we should check for both the old and new versions.
    152     if (value == '<multiline>') {
    153       // Skip start delimiter.
    154       if (i == len - 1 ||
    155           lines[++i].indexOf(DELIM_START) == -1)
    156         return false;
    157 
    158       ++i;
    159       value = '';
    160       // Append lines between start and end delimiters.
    161       while (i < len && lines[i] != DELIM_END)
    162         value += lines[i++] + '\n';
    163 
    164       // Remove trailing newline.
    165       if (value)
    166         value = value.substr(0, value.length - 1);
    167     }
    168     details.push({'statName': name, 'statValue': value});
    169   }
    170 
    171   var templateData = {'details': details};
    172   i18nTemplate.process(document, templateData);
    173   jstProcess(new JsEvalContext(templateData), $('t'));
    174 
    175   collapseMultiLineStrings();
    176   return true;
    177 }
    178 
    179 document.addEventListener('DOMContentLoaded', function() {
    180   jstProcess(loadTimeData.createJsEvalContext(), $('t'));
    181 
    182   $('collapseAll').onclick = collapseAll;
    183   $('expandAll').onclick = expandAll;
    184 
    185   var tp = $('t');
    186   tp.addEventListener('dragover', handleDragOver, false);
    187   tp.addEventListener('drop', handleDrop, false);
    188 
    189   collapseMultiLineStrings();
    190 });
    191