diff options
Diffstat (limited to 'chromium/chrome/browser/resources/sync_internals/sync_node_browser.js')
-rw-r--r-- | chromium/chrome/browser/resources/sync_internals/sync_node_browser.js | 259 |
1 files changed, 146 insertions, 113 deletions
diff --git a/chromium/chrome/browser/resources/sync_internals/sync_node_browser.js b/chromium/chrome/browser/resources/sync_internals/sync_node_browser.js index 8d1ff3d1be0..369916457a5 100644 --- a/chromium/chrome/browser/resources/sync_internals/sync_node_browser.js +++ b/chromium/chrome/browser/resources/sync_internals/sync_node_browser.js @@ -6,52 +6,83 @@ // require: cr/ui.js // require: cr/ui/tree.js -cr.define('chrome.sync', function() { +(function() { /** - * Gets all children of the given node and passes it to the given - * callback. - * @param {string} id The id whose children we want. - * @param {function(Array.<!Object>)} callback The callback to call - * with the list of children summaries. + * A helper function to determine if a node is the root of its type. + * + * @param {!Object} node The node to check. */ - function getSyncNodeChildrenSummaries(id, callback) { - var timer = chrome.sync.makeTimer(); - chrome.sync.getChildNodeIds(id, function(childNodeIds) { - console.debug('getChildNodeIds took ' + - timer.elapsedSeconds + 's to retrieve ' + - childNodeIds.length + ' ids'); - timer = chrome.sync.makeTimer(); - chrome.sync.getNodeSummariesById( - childNodeIds, function(childrenSummaries) { - console.debug('getNodeSummariesById took ' + - timer.elapsedSeconds + 's to retrieve summaries for ' + - childrenSummaries.length + ' nodes'); - callback(childrenSummaries); - }); - }); + var isTypeRootNode = function(node) { + return node.PARENT_ID == 'r' && node.UNIQUE_SERVER_TAG != ''; + } + + /** + * A helper function to determine if a node is a child of the given parent. + * + * @param {string} parentId The ID of the parent. + * @param {!Object} node The node to check. + */ + var isChildOf = function(parentId, node) { + return node.PARENT_ID == parentId; + } + + /** + * A helper function to sort sync nodes. + * + * Sorts by position index if possible, falls back to sorting by name, and + * finally sorting by METAHANDLE. + * + * If this proves to be slow and expensive, we should experiment with moving + * this functionality to C++ instead. + */ + var nodeComparator = function(nodeA, nodeB) { + if (nodeA.hasOwnProperty('positionIndex') && + nodeB.hasOwnProperty('positionIndex')) { + return nodeA.positionIndex - nodeB.positionIndex; + } else if (nodeA.NON_UNIQUE_NAME != nodeB.NON_UNIQUE_NAME) { + return nodeA.NON_UNIQUE_NAME.localeCompare(nodeB.NON_UNIQUE_NAME); + } else { + return nodeA.METAHANDLE - nodeB.METAHANDLE; + } + } + + /** + * Updates the node detail view with the details for the given node. + * @param {!Object} node The struct representing the node we want to display. + */ + function updateNodeDetailView(node) { + var nodeDetailsView = $('node-details'); + nodeDetailsView.hidden = false; + jstProcess(new JsEvalContext(node.entry_), nodeDetailsView); + } + + /** + * Updates the 'Last refresh time' display. + * @param {string} The text to display. + */ + function setLastRefreshTime(str) { + $('node-browser-refresh-time').textContent = str; } /** * Creates a new sync node tree item. - * @param {{id: string, title: string, isFolder: boolean}} - * nodeSummary The nodeSummary object for the node (as returned - * by chrome.sync.getNodeSummariesById()). + * * @constructor + * @param {!Object} node The nodeDetails object for the node as returned by + * chrome.sync.getAllNodes(). * @extends {cr.ui.TreeItem} */ - var SyncNodeTreeItem = function(nodeSummary) { - var treeItem = new cr.ui.TreeItem({ - id_: nodeSummary.id - }); + var SyncNodeTreeItem = function(node) { + var treeItem = new cr.ui.TreeItem(); treeItem.__proto__ = SyncNodeTreeItem.prototype; - treeItem.label = nodeSummary.title; - if (nodeSummary.isFolder) { + treeItem.entry_ = node; + treeItem.label = node.NON_UNIQUE_NAME; + if (node.IS_DIR) { treeItem.mayHaveChildren_ = true; - // Load children asynchronously on expand. - // TODO(akalin): Add a throbber while loading? - treeItem.triggeredLoad_ = false; + // Load children on expand. + treeItem.expanded_ = false; treeItem.addEventListener('expand', treeItem.handleExpand_.bind(treeItem)); } else { @@ -64,55 +95,31 @@ cr.define('chrome.sync', function() { __proto__: cr.ui.TreeItem.prototype, /** - * Retrieves the details for this node. - * @param {function(Object)} callback The callback that will be - * called with the node details, or null if it could not be - * retrieved. + * Finds the children of this node and appends them to the tree. */ - getDetails: function(callback) { - chrome.sync.getNodeDetailsById([this.id_], function(nodeDetails) { - callback(nodeDetails[0] || null); - }); - }, - handleExpand_: function(event) { - if (!this.triggeredLoad_) { - getSyncNodeChildrenSummaries(this.id_, this.addChildNodes_.bind(this)); - this.triggeredLoad_ = true; - } - }, + var treeItem = this; - /** - * Adds children from the list of children summaries. - * @param {Array.<{id: string, title: string, isFolder: boolean}>} - * childrenSummaries The list of children summaries with which - * to create the child nodes. - */ - addChildNodes_: function(childrenSummaries) { - var timer = chrome.sync.makeTimer(); - for (var i = 0; i < childrenSummaries.length; ++i) { - var childTreeItem = new SyncNodeTreeItem(childrenSummaries[i]); - this.add(childTreeItem); + if (treeItem.expanded_) { + return; } - console.debug('adding ' + childrenSummaries.length + - ' children took ' + timer.elapsedSeconds + 's'); - } - }; + treeItem.expanded_ = true; - /** - * Updates the node detail view with the details for the given node. - * @param {!Object} nodeDetails The details for the node we want - * to display. - */ - function updateNodeDetailView(nodeDetails) { - var nodeBrowser = document.getElementById('node-browser'); - // TODO(akalin): Write a nicer detail viewer. - nodeDetails.entry = JSON.stringify(nodeDetails.entry, null, 2); - jstProcess(new JsEvalContext(nodeDetails), nodeBrowser); - } + var children = treeItem.tree.allNodes.filter( + isChildOf.bind(undefined, treeItem.entry_.ID)); + children.sort(nodeComparator); + + children.forEach(function(node) { + treeItem.add(new SyncNodeTreeItem(node)); + }); + }, + }; /** - * Creates a new sync node tree. + * Creates a new sync node tree. Technically, it's a forest since it each + * type has its own root node for its own tree, but it still looks and acts + * mostly like a tree. + * * @param {Object=} opt_propertyBag Optional properties. * @constructor * @extends {cr.ui.Tree} @@ -125,57 +132,83 @@ cr.define('chrome.sync', function() { decorate: function() { cr.ui.Tree.prototype.decorate.call(this); this.addEventListener('change', this.handleChange_.bind(this)); - chrome.sync.getRootNodeDetails(this.makeRoot_.bind(this)); + this.allNodes = []; }, - /** - * Creates the root of the tree. - * @param {{id: string, title: string, isFolder: boolean}} - * rootNodeSummary The summary info for the root node. - */ - makeRoot_: function(rootNodeSummary) { - // The root node usually doesn't have a title. - rootNodeSummary.title = rootNodeSummary.title || 'Root'; - var rootTreeItem = new SyncNodeTreeItem(rootNodeSummary); - this.add(rootTreeItem); + populate: function(nodes) { + var tree = this; + + // We store the full set of nodes in the SyncNodeTree object. + tree.allNodes = nodes; + + var roots = tree.allNodes.filter(isTypeRootNode); + roots.sort(nodeComparator); + + roots.forEach(function(typeRoot) { + tree.add(new SyncNodeTreeItem(typeRoot)); + }); }, handleChange_: function(event) { if (this.selectedItem) { - this.selectedItem.getDetails(updateNodeDetailView); + updateNodeDetailView(this.selectedItem); } } }; - function decorateSyncNodeBrowser(syncNodeBrowser) { - cr.ui.decorate(syncNodeBrowser, SyncNodeTree); + /** + * Clears any existing UI state. Useful prior to a refresh. + */ + function clear() { + var treeContainer = $('sync-node-tree-container'); + while (treeContainer.firstChild) { + treeContainer.removeChild(treeContainer.firstChild); + } + + var nodeDetailsView = $('node-details'); + nodeDetailsView.hidden = true; } - // This is needed because JsTemplate (which is needed by - // updateNodeDetailView) is loaded at the end of the file after - // everything else. - // - // TODO(akalin): Remove dependency on JsTemplate and get rid of - // this. - var domLoaded = false; - var pendingSyncNodeBrowsers = []; - function decorateSyncNodeBrowserAfterDOMLoad(id) { - var e = document.getElementById(id); - if (domLoaded) { - decorateSyncNodeBrowser(e); - } else { - pendingSyncNodeBrowsers.push(e); - } + /** + * Fetch the latest set of nodes and refresh the UI. + */ + function refresh() { + $('node-browser-refresh-button').disabled = true; + + clear(); + setLastRefreshTime('In progress since ' + (new Date()).toLocaleString()); + + chrome.sync.getAllNodes(function(nodeMap) { + // Put all nodes into one big list that ignores the type. + var nodes = nodeMap. + map(function(x) { return x.nodes; }). + reduce(function(a, b) { return a.concat(b); }); + + var treeContainer = $('sync-node-tree-container'); + var tree = document.createElement('tree'); + tree.setAttribute('id', 'sync-node-tree'); + tree.setAttribute('icon-visibility', 'parent'); + treeContainer.appendChild(tree); + + cr.ui.decorate(tree, SyncNodeTree); + tree.populate(nodes); + + setLastRefreshTime((new Date()).toLocaleString()); + $('node-browser-refresh-button').disabled = false; + }); } - document.addEventListener('DOMContentLoaded', function() { - for (var i = 0; i < pendingSyncNodeBrowsers.length; ++i) { - decorateSyncNodeBrowser(pendingSyncNodeBrowsers[i]); - } - domLoaded = true; + document.addEventListener('DOMContentLoaded', function(e) { + $('node-browser-refresh-button').addEventListener('click', refresh); + cr.ui.decorate('#sync-node-splitter', cr.ui.Splitter); + + // Automatically trigger a refresh the first time this tab is selected. + $('sync-browser-tab').addEventListener('selectedChange', function f(e) { + if (this.selected) { + $('sync-browser-tab').removeEventListener('selectedChange', f); + refresh(); + } + }); }); - return { - decorateSyncNodeBrowser: decorateSyncNodeBrowserAfterDOMLoad - }; -}); +})(); |