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() { 6var dumpToTextButton = $('dump-to-text'); 7var dataDump = $('data-dump'); 8dumpToTextButton.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 35var 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 52function 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 */ 64function 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 */ 90function 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 100function makeBlobUrl(data) { 101 var textBlob = new Blob([data], {type: 'octet/stream'}); 102 var blobUrl = window.URL.createObjectURL(textBlob); 103 return blobUrl; 104} 105 106function 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 119function 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 */ 132function 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 159function 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 181function 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 190document.addEventListener('DOMContentLoaded', function() { 191 chrome.sync.events.addEventListener( 192 'onReceivedListOfTypes', 193 onReceivedListOfTypes); 194 chrome.sync.requestListOfTypes(); 195}); 196 197var dumpToFileLink = $('dump-to-file'); 198dumpToFileLink.addEventListener('click', function(event) { 199 chrome.sync.getAllNodes(triggerDataDownload); 200}); 201})(); 202