Chromium Code Reviews| Index: chrome/browser/resources/extensions/extension_error.js |
| diff --git a/chrome/browser/resources/extensions/extension_error.js b/chrome/browser/resources/extensions/extension_error.js |
| index 5d76630bd73ebd061649d3097e3cfb819547520c..68fba68bcfefbafa2c1d61a9b10633393b37ea1a 100644 |
| --- a/chrome/browser/resources/extensions/extension_error.js |
| +++ b/chrome/browser/resources/extensions/extension_error.js |
| @@ -46,7 +46,14 @@ cr.define('extensions', function() { |
| /** @override */ |
| getEquivalentElement: function(element) { |
| - return assert(this.querySelector('.extension-error-view-details')); |
| + if (element.classList.contains('extension-error-metadata')) |
| + return this; |
| + if (element.classList.contains('error-delete-button')) { |
| + return /** @type {!HTMLElement} */ (this.querySelector( |
| + '.error-delete-button')); |
| + } |
| + assertNotReached(); |
| + return element; |
| }, |
| /** |
| @@ -58,6 +65,12 @@ cr.define('extensions', function() { |
| decorate: function(error, boundary) { |
|
Dan Beam
2015/04/22 21:02:38
this code is out of date
Devlin
2015/04/22 23:17:31
Resynced.
|
| cr.ui.FocusRow.prototype.decorate.call(this, boundary); |
| + /** |
| + * The backing error. |
| + * @type {(ManifestError|RuntimeError)} |
| + */ |
| + this.error = error; |
| + |
| // Add an additional class for the severity level. |
| if (error.type == chrome.developerPrivate.ErrorType.RUNTIME) { |
| switch (error.severity) { |
| @@ -87,28 +100,35 @@ cr.define('extensions', function() { |
| var messageSpan = this.querySelector('.extension-error-message'); |
| messageSpan.textContent = error.message; |
| - messageSpan.title = error.message; |
| - var extensionUrl = 'chrome-extension://' + error.extensionId + '/'; |
| - var viewDetailsLink = this.querySelector('.extension-error-view-details'); |
| + var deleteButton = this.querySelector('.error-delete-button'); |
| + deleteButton.addEventListener('click', function(e) { |
| + this.dispatchEvent( |
| + new CustomEvent('deleteExtensionError', |
| + {bubbles: true, detail: this.error})); |
| + }.bind(this)); |
| - // If we cannot open the file source and there are no external frames in |
| - // the stack, then there are no details to display. |
| - if (!extensions.ExtensionErrorOverlay.canShowOverlayForError( |
| - error, extensionUrl)) { |
| - viewDetailsLink.hidden = true; |
| - } else { |
| - var stringId = extensionUrl.toLowerCase() == 'manifest.json' ? |
| - 'extensionErrorViewManifest' : 'extensionErrorViewDetails'; |
| - viewDetailsLink.textContent = loadTimeData.getString(stringId); |
| + this.addEventListener('click', function(e) { |
| + if (e.target != deleteButton) |
| + this.requestActive_(); |
| + }.bind(this)); |
| + this.addEventListener('keydown', function(e) { |
| + if (e.keyIdentifier == 'Enter') |
|
Dan Beam
2015/04/22 21:02:38
probably need target != deleteButton here as well
Devlin
2015/04/22 23:17:31
Done.
|
| + this.requestActive_(); |
| + }); |
| - viewDetailsLink.addEventListener('click', function(e) { |
| - extensions.ExtensionErrorOverlay.getInstance().setErrorAndShowOverlay( |
| - error, extensionUrl); |
| - }); |
| + this.addFocusableElement(this); |
| + this.addFocusableElement(this.querySelector('.error-delete-button')); |
| + }, |
| - this.addFocusableElement(viewDetailsLink); |
| - } |
| + /** |
| + * Bubble up an event to request to become active. |
| + * @private |
| + */ |
| + requestActive_: function() { |
| + this.dispatchEvent( |
| + new CustomEvent('highlightExtensionError', |
| + {bubbles: true, detail: this.error})); |
| }, |
| }; |
| @@ -116,137 +136,205 @@ cr.define('extensions', function() { |
| * A variable length list of runtime or manifest errors for a given extension. |
| * @param {Array<(RuntimeError|ManifestError)>} errors The list of extension |
| * errors with which to populate the list. |
| + * @param {string} extensionId The id of the extension. |
| * @constructor |
| * @extends {HTMLDivElement} |
| */ |
| - function ExtensionErrorList(errors) { |
| + function ExtensionErrorList(errors, extensionId) { |
| var div = cloneTemplate('extension-error-list'); |
| div.__proto__ = ExtensionErrorList.prototype; |
| - div.errors_ = errors; |
| - div.decorate(); |
| + div.extensionId_ = extensionId; |
| + div.decorate(errors); |
| return div; |
| } |
| - /** |
| - * @private |
| - * @const |
| - * @type {number} |
| - */ |
| - ExtensionErrorList.MAX_ERRORS_TO_SHOW_ = 3; |
| - |
| ExtensionErrorList.prototype = { |
| __proto__: HTMLDivElement.prototype, |
| - decorate: function() { |
| + /** |
| + * Initializes the extension error list. |
| + * @param {Array<(RuntimeError|ManifestError)>} errors The list of errors. |
| + */ |
| + decorate: function(errors) { |
| + /** |
| + * @private {Array<(ManifestError|RuntimeError)>} |
| + */ |
| + this.errors_ = []; |
| + |
| this.focusGrid_ = new cr.ui.FocusGrid(); |
| this.gridBoundary_ = this.querySelector('.extension-error-list-contents'); |
| this.gridBoundary_.addEventListener('focus', this.onFocus_.bind(this)); |
| this.gridBoundary_.addEventListener('focusin', |
| this.onFocusin_.bind(this)); |
| - this.errors_.forEach(function(error) { |
| - if (idIsValid(error.extensionId)) { |
| - var focusRow = new ExtensionError(error, this.gridBoundary_); |
| - this.gridBoundary_.appendChild( |
| - document.createElement('li')).appendChild(focusRow); |
| - this.focusGrid_.addRow(focusRow); |
| + errors.forEach(this.addError_, this); |
| + |
| + this.addEventListener('highlightExtensionError', function(e) { |
| + this.setActiveErrorNode_(e.target); |
| + }); |
| + this.addEventListener('deleteExtensionError', function(e) { |
| + this.removeError_(e.detail); |
| + }); |
| + |
| + this.querySelector('#extension-error-list-clear').addEventListener( |
| + 'click', function(e) { |
| + this.clear(); |
| + }.bind(this)); |
| + |
| + chrome.developerPrivate.onItemStateChanged.addListener( |
| + function(data) { |
| + var type = chrome.developerPrivate.EventType; |
| + if ((data.event_type == type.ERRORS_REMOVED || |
| + data.event_type == type.ERROR_ADDED) && |
| + data.info.id == this.extensionId_) { |
| + var newErrors = |
| + data.info.runtimeErrors.concat(data.info.manifestErrors); |
| + this.updateErrors_(newErrors); |
| } |
| - }, this); |
| + }.bind(this)); |
| + |
| + /** |
| + * The active error element in the list. |
| + * @private {?} |
| + */ |
| + this.activeError_ = null; |
| - var numShowing = this.focusGrid_.rows.length; |
| - if (numShowing > ExtensionErrorList.MAX_ERRORS_TO_SHOW_) |
| - this.initShowMoreLink_(); |
| + this.setActiveError(0); |
| }, |
| /** |
| - * @return {?Element} The element that toggles between show more and show |
| - * less, or null if it's hidden. Button will be hidden if there are less |
| - * errors than |MAX_ERRORS_TO_SHOW_|. |
| + * Adds an error to the list. |
| + * @param {(RuntimeError|ManifestError)} error The error to add. |
| + * @private |
| */ |
| - getToggleElement: function() { |
| - return this.querySelector( |
| - '.extension-error-list-show-more [is="action-link"]:not([hidden])'); |
| - }, |
| - |
| - /** @return {!Element} The element containing the list of errors. */ |
| - getErrorListElement: function() { |
| - return this.gridBoundary_; |
| + addError_: function(error) { |
| + this.errors_.push(error); |
| + var focusRow = new ExtensionError(error, this.gridBoundary_); |
| + this.gridBoundary_.appendChild(document.createElement('li')). |
| + appendChild(focusRow); |
| + this.focusGrid_.addRow(focusRow); |
| }, |
| /** |
| - * The grid should not be focusable once it or an element inside it is |
| - * focused. This is necessary to allow tabbing out of the grid in reverse. |
| + * Removes an error from the list. |
| + * @param {(RuntimeError|ManifestError)} error The error to remove. |
| * @private |
| */ |
| - onFocusin_: function() { |
| - this.gridBoundary_.tabIndex = -1; |
| + removeError_: function(error) { |
| + var index = this.errors_.indexOf(error); |
|
Dan Beam
2015/04/22 21:02:38
using references as IDs is kinda dubious. will er
Devlin
2015/04/22 23:17:31
Hmm... in theory, no, but in a pass-by-reference l
|
| + var errorList = this.querySelector('.extension-error-list-contents'); |
| + |
| + var wasActive = this.activeError_ && this.activeError_.error == error; |
|
Dan Beam
2015/04/22 21:02:38
same dubiousness with == across objects
Devlin
2015/04/22 23:17:31
Done.
|
| + |
| + this.errors_.splice(index, 1); |
| + var listElement = errorList.children[index]; |
| + listElement.parentNode.removeChild(listElement); |
| + |
| + if (wasActive) { |
| + index = Math.min(index, this.errors_.length - 1); |
| + this.setActiveError(index); // Gracefully handles the -1 case. |
| + } |
| + |
| + chrome.developerPrivate.deleteExtensionErrors({ |
| + extensionId: error.extensionId, |
| + errorIds: [error.id] |
| + }); |
| }, |
| /** |
| - * Focus the first focusable row when tabbing into the grid for the |
| - * first time. |
| + * Updates the list of errors. |
| + * @param {Array<(ManifestError|RuntimeError)>} newErrors The new list of |
| + * errors. |
| * @private |
| */ |
| - onFocus_: function() { |
| - var activeRow = this.gridBoundary_.querySelector('.focus-row-active'); |
| - var toggleButton = this.getToggleElement(); |
| - |
| - if (toggleButton && !toggleButton.isShowingAll) { |
| - var rows = this.focusGrid_.rows; |
| - assert(rows.length > ExtensionErrorList.MAX_ERRORS_TO_SHOW_); |
| + updateErrors_: function(newErrors) { |
| + var listHasError = function(list, id) { |
| + for (var i = 0; i < list.length; ++i) { |
| + if (list[i].id == id) |
| + return true; |
| + } |
| + return false; |
| + }; |
| + this.errors_.forEach(function(error) { |
| + if (!listHasError(newErrors, error.id)) |
| + this.removeError_(error); |
| + }, this); |
| + newErrors.forEach(function(error) { |
| + if (!listHasError(this.errors_, error.id)) |
| + this.addError_(error); |
| + }, this); |
| + }, |
| - var firstVisible = rows.length - ExtensionErrorList.MAX_ERRORS_TO_SHOW_; |
| - if (rows.indexOf(activeRow) < firstVisible) |
| - activeRow = rows[firstVisible]; |
| - } else if (!activeRow) { |
| - activeRow = this.focusGrid_.rows[0]; |
| + /** |
| + * Sets the active error in the list. |
| + * @param {number} index The index to set to be active. |
| + */ |
| + setActiveError: function(index) { |
| + var errorList = this.querySelector('.extension-error-list-contents'); |
| + var node = null; |
| + if (index >= 0 && index < errorList.children.length) { |
|
Dan Beam
2015/04/22 21:02:38
nit:
var item = errorList.children[index];
if
Devlin
2015/04/22 23:17:31
We also want to call setActiveErrorNode_ with null
|
| + node = errorList.children[index].querySelector( |
| + '.extension-error-metadata'); |
|
Dan Beam
2015/04/22 21:02:38
nit:
node = errorList.children[index].querySele
Devlin
2015/04/22 23:17:31
Moot with above, but noted. :)
|
| } |
| + this.setActiveErrorNode_(node); |
| + }, |
| - activeRow.getEquivalentElement(null).focus(); |
| + /** |
| + * Clears the list of all errors. |
| + */ |
| + clear: function() { |
| + if (this.errors_.length == 0) |
| + return; |
| + |
| + var ids = this.errors_.map(function(error) { return error.id; }); |
| + chrome.developerPrivate.deleteExtensionErrors({ |
| + extensionId: this.extensionId_, |
| + errorIds: ids |
| + }); |
| + |
| + this.setActiveErrorNode_(null); |
| + this.errors_.length = 0; |
| + var errorList = this.querySelector('.extension-error-list-contents'); |
|
Dan Beam
2015/04/22 21:02:38
nit:
this.querySelector('.extension-error-list-
Devlin
2015/04/22 23:17:31
Where'd that comment go....
https://codereview.chr
|
| + while (errorList.firstChild) |
| + errorList.removeChild(errorList.firstChild); |
| }, |
| /** |
| - * Initialize the "Show More" link for the error list. If there are more |
| - * than |MAX_ERRORS_TO_SHOW_| errors in the list. |
| + * Sets the active error in the list. |
| + * @param {?} node The error to make active. |
| * @private |
| */ |
| - initShowMoreLink_: function() { |
| - var link = this.querySelector( |
| - '.extension-error-list-show-more [is="action-link"]'); |
| - link.hidden = false; |
| - link.isShowingAll = false; |
| - |
| - var listContents = this.querySelector('.extension-error-list-contents'); |
| - |
| - // TODO(dbeam/kalman): trade all this transition voodoo for .animate()? |
| - listContents.addEventListener('webkitTransitionEnd', function(e) { |
| - if (listContents.classList.contains('deactivating')) |
| - listContents.classList.remove('deactivating', 'active'); |
| - else |
| - listContents.classList.add('scrollable'); |
| - }); |
| + setActiveErrorNode_: function(node) { |
| + if (this.activeError_) |
| + this.activeError_.classList.remove('extension-error-active'); |
| - link.addEventListener('click', function(e) { |
| - // Needs to be enabled in case the focused row is now hidden. |
| - this.gridBoundary_.tabIndex = 0; |
| + if (node) |
| + node.classList.add('extension-error-active'); |
| - link.isShowingAll = !link.isShowingAll; |
| + this.activeError_ = node; |
| - var message = link.isShowingAll ? 'extensionErrorsShowFewer' : |
| - 'extensionErrorsShowMore'; |
| - link.textContent = loadTimeData.getString(message); |
| + this.dispatchEvent( |
| + new CustomEvent('activeExtensionErrorChanged', |
| + {bubbles: true, detail: node ? node.error : null})); |
| + }, |
| - // Disable scrolling while transitioning. If the element is active, |
| - // scrolling is enabled when the transition ends. |
| - listContents.classList.remove('scrollable'); |
| + /** |
| + * The grid should not be focusable once it or an element inside it is |
| + * focused. This is necessary to allow tabbing out of the grid in reverse. |
| + * @private |
| + */ |
| + onFocusin_: function() { |
| + this.gridBoundary_.tabIndex = -1; |
| + }, |
| - if (link.isShowingAll) { |
| - listContents.classList.add('active'); |
| - listContents.classList.remove('deactivating'); |
| - } else { |
| - listContents.classList.add('deactivating'); |
| - } |
| - }.bind(this)); |
| - } |
| + /** |
| + * Focus the first focusable row when tabbing into the grid for the |
| + * first time. |
| + * @private |
| + */ |
| + onFocus_: function() { |
| + var activeRow = this.gridBoundary_.querySelector('.focus-row-active'); |
| + activeRow.getEquivalentElement(null).focus(); |
| + }, |
| }; |
| return { |