Index: chrome/browser/resources/downloads/downloads.js |
diff --git a/chrome/browser/resources/downloads/downloads.js b/chrome/browser/resources/downloads/downloads.js |
index fd7ccc9e6dee71dd48d02317fc2fb38d95c60fa6..8bc2c6f780eedb4e081412cc3854318f37254e25 100644 |
--- a/chrome/browser/resources/downloads/downloads.js |
+++ b/chrome/browser/resources/downloads/downloads.js |
@@ -77,6 +77,48 @@ function createButton(onclick, value) { |
} |
/////////////////////////////////////////////////////////////////////////////// |
+// DownloadFocusObserver: |
+ |
+/** |
+ * @constructor |
+ * @implements {cr.ui.FocusRow.Observer} |
+ */ |
+function DownloadFocusObserver() {} |
+ |
+DownloadFocusObserver.prototype = { |
+ /** @override */ |
+ onActivate: function(row) {}, |
+ |
+ /** @override */ |
+ onDeactivate: function(row) {}, |
+ |
+ /** |
+ * @param {Element} item The row item to find a a row element for. |
+ * @return {Element} |row|'s "active" element. |
+ * @override |
+ */ |
+ getRowElement: function(item) { |
+ return findAncestorByClass(item, 'download'); |
+ }, |
+ |
+ /** @override */ |
+ onElementIdMiss: function(row, expectedId) { |
+ // These are at the same level visually, so focus the first available one if |
+ // it exists. |
+ var remaining = ['show', 'retry', 'pause', 'resume', 'remove', 'cancel']; |
+ if (remaining.indexOf(expectedId) > -1) { |
+ for (var i = 0; i < remaining.length; ++i) { |
+ if (row.elementIds.indexOf(remaining[i]) > -1) |
+ return remaining[i]; |
+ } |
+ } |
+ |
+ // Select the first item that is focusable if nothing else worked. |
+ return row.elementIds[0]; |
+ }, |
+}; |
+ |
+/////////////////////////////////////////////////////////////////////////////// |
// Downloads |
/** |
* Class to hold all the information about the visible downloads. |
@@ -91,6 +133,8 @@ function Downloads() { |
this.node_ = $('downloads-display'); |
this.summary_ = $('downloads-summary-text'); |
this.searchText_ = ''; |
+ this.focusGrid_ = new cr.ui.FocusGrid(this.node_, |
+ new DownloadFocusObserver()); |
// Keep track of the dates of the newest and oldest downloads so that we |
// know where to insert them. |
@@ -176,6 +220,77 @@ Downloads.prototype.updateResults = function() { |
if (loadTimeData.getBoolean('allow_deleting_history')) |
$('clear-all').hidden = !hasDownloads || this.searchText_.length > 0; |
+ |
+ this.rebuildFocusGrid_(); |
+}; |
+ |
+/** |
+ * Determines if element should be focusable. |
+ * @param {!Element} element |
+ * @return {bool} |
+ * @private |
+ */ |
+Downloads.shouldFocus_ = function(element) { |
+ if (!element) |
+ return false; |
+ |
+ // Hidden elements are not focusable. |
+ var style = window.getComputedStyle(element); |
+ if (style.visibility == 'hidden' || style.display == 'none') |
+ return false; |
+ |
+ // Verify that all ancestors are focusable. |
+ if (element.parentElement) |
+ return Downloads.shouldFocus_(element.parentElement); |
+ |
+ return true; |
+}; |
+ |
+/** |
+ * Will add an element to |focusRow| if it can be focused. |
+ * @param {cr.ui.FocusRow} focusRow The row to add the element to. |
+ * @param {Element} element The element to add. |
+ * @param {string} elementId The elementId used for selecting in a grid. |
+ * @private |
+ */ |
+Downloads.prototype.addRowElement_ = function(focusRow, element, elementId) { |
+ if (Downloads.shouldFocus_(element)) |
+ focusRow.setFocusableElementId(element, elementId); |
+}; |
+ |
+/** |
+ * Rebuild the focusGrid_ using the elements that each download will have. |
+ * @private |
+ */ |
+Downloads.prototype.rebuildFocusGrid_ = function() { |
+ this.focusGrid_.destroy(); |
+ |
+ // Keys for downloads are in the reverse order of what is displayed. |
+ var keys = Object.keys(this.downloads_); |
+ for (var i = keys.length - 1; i >= 0; --i) { |
+ var node = this.downloads_[keys[i]]; |
+ var focusRow = this.focusGrid_.createRow(); |
+ |
+ // Add all clickable elements as a row into the grid. |
+ this.addRowElement_(focusRow, node.nodeFileLink_, 'name'); |
+ this.addRowElement_(focusRow, node.nodeURL_, 'url'); |
+ this.addRowElement_(focusRow, node.controlShow_, 'show'); |
+ this.addRowElement_(focusRow, node.controlRetry_, 'retry'); |
+ this.addRowElement_(focusRow, node.controlPause_, 'pause'); |
+ this.addRowElement_(focusRow, node.controlResume_, 'resume'); |
+ this.addRowElement_(focusRow, node.controlRemove_, 'remove'); |
+ this.addRowElement_(focusRow, node.controlCancel_, 'cancel'); |
+ |
+ // These buttons are mutually exclusive, but they are visually the same. |
+ this.addRowElement_(focusRow, node.malwareSave_, 'save'); |
+ this.addRowElement_(focusRow, node.dangerSave_, 'save'); |
+ this.addRowElement_(focusRow, node.malwareDiscard_, 'discard'); |
+ this.addRowElement_(focusRow, node.dangerDiscard_, 'discard'); |
+ |
+ this.addRowElement_(focusRow, node.controlByExtensionLink_, 'extension'); |
+ |
+ this.focusGrid_.addRow(focusRow); |
+ } |
}; |
/** |