// Copyright (c) 2011 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. (function() { var dumpToTextButton = $('dump-to-text'); var dataDump = $('data-dump'); dumpToTextButton.addEventListener('click', function(event) { // TODO(akalin): Add info like Chrome version, OS, date dumped, etc. var data = ''; data += '======\n'; data += 'Status\n'; data += '======\n'; data += JSON.stringify(chrome.sync.aboutInfo, null, 2); data += '\n'; data += '\n'; data += '=============\n'; data += 'Notifications\n'; data += '=============\n'; data += JSON.stringify(chrome.sync.notifications, null, 2); data += '\n'; data += '\n'; data += '===\n'; data += 'Log\n'; data += '===\n'; data += JSON.stringify(chrome.sync.log.entries, null, 2); data += '\n'; dataDump.textContent = data; }); var allFields = [ 'ID', 'IS_UNSYNCED', 'IS_UNAPPLIED_UPDATE', 'BASE_VERSION', 'BASE_VERSION_TIME', 'SERVER_VERSION', 'SERVER_VERSION_TIME', 'PARENT_ID', 'SERVER_PARENT_ID', 'IS_DEL', 'SERVER_IS_DEL', 'serverModelType', 'SERVER_SPECIFICS', 'SPECIFICS', ]; function versionToDateString(version) { // TODO(mmontgomery): ugly? Hacky? Is there a better way? var epochLength = Date.now().toString().length; var epochTime = parseInt(version.slice(0, epochLength)); var date = new Date(epochTime); return date.toString(); } /** * @param {!Object} node A JavaScript represenation of a sync entity. * @return {string} A string representation of the sync entity. */ function serializeNode(node) { return allFields.map(function(field) { var fieldVal; if (field == 'SERVER_VERSION_TIME') { var version = node['SERVER_VERSION']; fieldVal = versionToDateString(version); } if (field == 'BASE_VERSION_TIME') { var version = node['BASE_VERSION']; fieldVal = versionToDateString(version); } else if ((field == 'SERVER_SPECIFICS' || field == 'SPECIFICS') && (!$('include-specifics').checked)) { fieldVal = 'REDACTED'; } else if ((field == 'SERVER_SPECIFICS' || field == 'SPECIFICS') && $('include-specifics').checked) { fieldVal = JSON.stringify(node[field]); } else { fieldVal = node[field]; } return fieldVal; }); } /** * @param {string} type The name of a sync model type. * @return {boolean} True if the type's checkbox is selected. */ function isSelectedDatatype(type) { var typeCheckbox = $(type); // Some types, such as 'Top level folder', appear in the list of nodes // but not in the list of selectable items. if (typeCheckbox == null) { return false; } return typeCheckbox.checked; } function makeBlobUrl(data) { var textBlob = new Blob([data], {type: 'octet/stream'}); var blobUrl = window.URL.createObjectURL(textBlob); return blobUrl; } function makeDownloadName() { // Format is sync-data-dump-$epoch-$year-$month-$day-$OS.csv. var now = new Date(); var friendlyDate = [now.getFullYear(), now.getMonth() + 1, now.getDate()].join('-'); var name = ['sync-data-dump', friendlyDate, Date.now(), navigator.platform].join('-'); return [name, 'csv'].join('.'); } function makeDateUserAgentHeader() { var now = new Date(); var userAgent = window.navigator.userAgent; var dateUaHeader = [now.toISOString(), userAgent].join(','); return dateUaHeader; } /** * Builds a summary of current state and exports it as a downloaded file. * * @param {!Array.<{type: string, nodes: !Array}>} nodesMap * Summary of local state by model type. */ function triggerDataDownload(nodesMap) { // Prepend a header with ISO date and useragent. var output = [makeDateUserAgentHeader()]; output.push('====='); var aboutInfo = JSON.stringify(chrome.sync.aboutInfo, null, 2); output.push(aboutInfo); // Filter out non-selected types. var selectedTypesNodes = nodesMap.filter(function(x) { return isSelectedDatatype(x.type); }); // Serialize the remaining nodes and add them to the output. selectedTypesNodes.forEach(function(typeNodes) { output.push('====='); output.push(typeNodes.nodes.map(serializeNode).join('\n')); }); output = output.join('\n'); var anchor = $('dump-to-file-anchor'); anchor.href = makeBlobUrl(output); anchor.download = makeDownloadName(); anchor.click(); } function createTypesCheckboxes(types) { var containerElt = $('node-type-checkboxes'); types.map(function(type) { var div = document.createElement('div'); var checkbox = document.createElement('input'); checkbox.id = type; checkbox.type = 'checkbox'; checkbox.checked = 'yes'; div.appendChild(checkbox); var label = document.createElement('label'); // Assigning to label.for doesn't work. label.setAttribute('for', type); label.innerText = type; div.appendChild(label); containerElt.appendChild(div); }); } function onReceivedListOfTypes(e) { var types = e.details.types; types.sort(); createTypesCheckboxes(types); chrome.sync.events.removeEventListener( 'onReceivedListOfTypes', onReceivedListOfTypes); } document.addEventListener('DOMContentLoaded', function() { chrome.sync.events.addEventListener( 'onReceivedListOfTypes', onReceivedListOfTypes); chrome.sync.requestListOfTypes(); }); var dumpToFileLink = $('dump-to-file'); dumpToFileLink.addEventListener('click', function(event) { chrome.sync.getAllNodes(triggerDataDownload); }); })();