Home | History | Annotate | Download | only in sync_internals
      1 // Copyright (c) 2011 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 (function() {
      6 var dumpToTextButton = $('dump-to-text');
      7 var dataDump = $('data-dump');
      8 dumpToTextButton.addEventListener('click', function(event) {
      9   // TODO(akalin): Add info like Chrome version, OS, date dumped, etc.
     10 
     11   var data = '';
     12   data += '======\n';
     13   data += 'Status\n';
     14   data += '======\n';
     15   data += JSON.stringify(chrome.sync.aboutInfo, null, 2);
     16   data += '\n';
     17   data += '\n';
     18 
     19   data += '=============\n';
     20   data += 'Notifications\n';
     21   data += '=============\n';
     22   data += JSON.stringify(chrome.sync.notifications, null, 2);
     23   data += '\n';
     24   data += '\n';
     25 
     26   data += '===\n';
     27   data += 'Log\n';
     28   data += '===\n';
     29   data += JSON.stringify(chrome.sync.log.entries, null, 2);
     30   data += '\n';
     31 
     32   dataDump.textContent = data;
     33 });
     34 
     35 var allFields = [
     36   'ID',
     37   'IS_UNSYNCED',
     38   'IS_UNAPPLIED_UPDATE',
     39   'BASE_VERSION',
     40   'BASE_VERSION_TIME',
     41   'SERVER_VERSION',
     42   'SERVER_VERSION_TIME',
     43   'PARENT_ID',
     44   'SERVER_PARENT_ID',
     45   'IS_DEL',
     46   'SERVER_IS_DEL',
     47   'serverModelType',
     48   'SERVER_SPECIFICS',
     49   'SPECIFICS',
     50 ];
     51 
     52 function versionToDateString(version) {
     53   // TODO(mmontgomery): ugly? Hacky? Is there a better way?
     54   var epochLength = Date.now().toString().length;
     55   var epochTime = parseInt(version.slice(0, epochLength));
     56   var date = new Date(epochTime);
     57   return date.toString();
     58 }
     59 
     60 /**
     61  * @param {!Object} node A JavaScript represenation of a sync entity.
     62  * @return {string} A string representation of the sync entity.
     63  */
     64 function serializeNode(node) {
     65   return allFields.map(function(field) {
     66     var fieldVal;
     67     if (field == 'SERVER_VERSION_TIME') {
     68       var version = node['SERVER_VERSION'];
     69       fieldVal = versionToDateString(version);
     70     } if (field == 'BASE_VERSION_TIME') {
     71       var version = node['BASE_VERSION'];
     72       fieldVal = versionToDateString(version);
     73     } else if ((field == 'SERVER_SPECIFICS' || field == 'SPECIFICS') &&
     74             (!$('include-specifics').checked)) {
     75       fieldVal = 'REDACTED';
     76     } else if ((field == 'SERVER_SPECIFICS' || field == 'SPECIFICS') &&
     77             $('include-specifics').checked) {
     78       fieldVal = JSON.stringify(node[field]);
     79     } else {
     80       fieldVal = node[field];
     81     }
     82     return fieldVal;
     83   });
     84 }
     85 
     86 /**
     87  * @param {string} type The name of a sync model type.
     88  * @return {boolean} True if the type's checkbox is selected.
     89  */
     90 function isSelectedDatatype(type) {
     91   var typeCheckbox = $(type);
     92   // Some types, such as 'Top level folder', appear in the list of nodes
     93   // but not in the list of selectable items.
     94   if (typeCheckbox == null) {
     95     return false;
     96   }
     97   return typeCheckbox.checked;
     98 }
     99 
    100 function makeBlobUrl(data) {
    101   var textBlob = new Blob([data], {type: 'octet/stream'});
    102   var blobUrl = window.URL.createObjectURL(textBlob);
    103   return blobUrl;
    104 }
    105 
    106 function makeDownloadName() {
    107   // Format is sync-data-dump-$epoch-$year-$month-$day-$OS.csv.
    108   var now = new Date();
    109   var friendlyDate = [now.getFullYear(),
    110                       now.getMonth() + 1,
    111                       now.getDate()].join('-');
    112   var name = ['sync-data-dump',
    113               friendlyDate,
    114               Date.now(),
    115               navigator.platform].join('-');
    116   return [name, 'csv'].join('.');
    117 }
    118 
    119 function makeDateUserAgentHeader() {
    120   var now = new Date();
    121   var userAgent = window.navigator.userAgent;
    122   var dateUaHeader = [now.toISOString(), userAgent].join(',');
    123   return dateUaHeader;
    124 }
    125 
    126 /**
    127  * Builds a summary of current state and exports it as a downloaded file.
    128  *
    129  * @param {!Array.<{type: string, nodes: !Array<!Object>}>} nodesMap
    130  *     Summary of local state by model type.
    131  */
    132 function triggerDataDownload(nodesMap) {
    133   // Prepend a header with ISO date and useragent.
    134   var output = [makeDateUserAgentHeader()];
    135   output.push('=====');
    136 
    137   var aboutInfo = JSON.stringify(chrome.sync.aboutInfo, null, 2);
    138   output.push(aboutInfo);
    139 
    140   // Filter out non-selected types.
    141   var selectedTypesNodes = nodesMap.filter(function(x) {
    142     return isSelectedDatatype(x.type);
    143   });
    144 
    145   // Serialize the remaining nodes and add them to the output.
    146   selectedTypesNodes.forEach(function(typeNodes) {
    147     output.push('=====');
    148     output.push(typeNodes.nodes.map(serializeNode).join('\n'));
    149   });
    150 
    151   output = output.join('\n');
    152 
    153   var anchor = $('dump-to-file-anchor');
    154   anchor.href = makeBlobUrl(output);
    155   anchor.download = makeDownloadName();
    156   anchor.click();
    157 }
    158 
    159 function createTypesCheckboxes(types) {
    160   var containerElt = $('node-type-checkboxes');
    161 
    162   types.map(function(type) {
    163     var div = document.createElement('div');
    164 
    165     var checkbox = document.createElement('input');
    166     checkbox.id = type;
    167     checkbox.type = 'checkbox';
    168     checkbox.checked = 'yes';
    169     div.appendChild(checkbox);
    170 
    171     var label = document.createElement('label');
    172     // Assigning to label.for doesn't work.
    173     label.setAttribute('for', type);
    174     label.innerText = type;
    175     div.appendChild(label);
    176 
    177     containerElt.appendChild(div);
    178   });
    179 }
    180 
    181 function onReceivedListOfTypes(e) {
    182   var types = e.details.types;
    183   types.sort();
    184   createTypesCheckboxes(types);
    185   chrome.sync.events.removeEventListener(
    186       'onReceivedListOfTypes',
    187       onReceivedListOfTypes);
    188 }
    189 
    190 document.addEventListener('DOMContentLoaded', function() {
    191   chrome.sync.events.addEventListener(
    192       'onReceivedListOfTypes',
    193       onReceivedListOfTypes);
    194   chrome.sync.requestListOfTypes();
    195 });
    196 
    197 var dumpToFileLink = $('dump-to-file');
    198 dumpToFileLink.addEventListener('click', function(event) {
    199   chrome.sync.getAllNodes(triggerDataDownload);
    200 });
    201 })();
    202