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..e592b1eb26457c3c9977da170b95dccfa5811f4b 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; |
|
Dan Beam
2015/03/31 00:33:49
nit: no if/else after return, i.e.
if (element.
Devlin
2015/03/31 16:17:50
Done.
|
| + } else 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) { |
| cr.ui.FocusRow.prototype.decorate.call(this, boundary); |
| + /** |
| + * The backing error. |
| + * @type {(ManifestError|RuntimeError)} |
| + */ |
| + this.error = error; |
|
Dan Beam
2015/03/31 00:33:50
I couldn't really tell, but can this be @private?
Devlin
2015/03/31 16:17:51
It's used by the ExtensionErrorList (in this patch
|
| + |
| // 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 })); |
|
Dan Beam
2015/03/31 00:33:49
{\s -> {
\s} -> }
(everywhere)
Devlin
2015/03/31 16:17:50
Done.
|
| + }.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.keyCode == 13 /* enter */) |
|
Dan Beam
2015/03/31 00:33:49
e.keyIdentifier == 'Enter'
Dan Beam
2015/03/31 00:33:50
isn't there already a click dispatched on Enter?
Devlin
2015/03/31 16:17:50
Thought keyIdentifier was from an old draft... Do
Devlin
2015/03/31 16:17:50
I would have thought so, but in testing, no. I wo
|
| + 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,60 +136,184 @@ 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); |
| + } |
| + }.bind(this)); |
| + |
| + /** |
| + * The active error element in the list. |
| + * @private {?} |
|
Devlin
2015/03/26 21:42:00
Making this @private {ExtensionError} results in "
Dan Beam
2015/03/31 00:33:50
extensions.ExtensionError
Devlin
2015/03/31 16:17:50
Thought of that and tried it, same error (well, "U
|
| + */ |
| + this.activeError_ = null; |
| + |
| + this.setActiveError(0); |
| + }, |
| + |
| + /** |
| + * Adds an error to the list. |
| + * @param {(RuntimeError|ManifestError)} error The error to add. |
| + * @private |
| + */ |
| + 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); |
| + }, |
| + |
| + /** |
| + * Removes an error from the list. |
| + * @param {(RuntimeError|ManifestError)} error The error to remove. |
| + * @private |
| + */ |
| + removeError_: function(error) { |
| + var index = this.errors_.indexOf(error); |
| + var errorList = this.querySelector('.extension-error-list-contents'); |
| + |
| + var wasActive = this.activeError_ && this.activeError_.error == error; |
| + |
| + this.errors_.splice(index, 1); |
| + var listElement = errorList.children[index]; |
| + listElement.parentNode.removeChild(listElement); |
| + |
| + if (wasActive) { |
| + index = Math.min(index, this.errors_.length - 1); |
|
Dan Beam
2015/03/31 00:33:50
what if this.errors_.length == 0? does -1 work?
Devlin
2015/03/31 16:17:50
Yep, line 274 (in this patch set) checks the index
|
| + this.setActiveError(index); |
| + } |
| + |
| + chrome.developerPrivate.deleteExtensionErrors({ |
| + extensionId: error.extensionId, |
| + errorIds: [error.id] |
| + }); |
| + }, |
| + |
| + /** |
| + * Updates the list of errors. |
| + * @param {Array<(ManifestError|RuntimeError)>} newErrors The new list of |
| + * errors. |
| + * @private |
| + */ |
| + updateErrors_: function(newErrors) { |
|
Dan Beam
2015/03/31 00:33:49
how often is this called? currently O(N) of M*N i
Devlin
2015/03/31 16:17:50
Yeah, right now it's M*N. M and N are each capped
|
| + 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 numShowing = this.focusGrid_.rows.length; |
| - if (numShowing > ExtensionErrorList.MAX_ERRORS_TO_SHOW_) |
| - this.initShowMoreLink_(); |
| + /** |
| + * 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; |
|
Dan Beam
2015/03/31 00:33:49
var node = errorList.children[index];
if (node)
Devlin
2015/03/31 16:17:50
That's actually different. setActiveErrorNode_()
|
| + if (index >= 0 && index < errorList.children.length) { |
| + node = errorList.children[index].querySelector( |
| + '.extension-error-metadata'); |
| + } |
| + this.setActiveErrorNode_(node); |
| }, |
| /** |
| - * @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_|. |
| + * Clears the list of all errors. |
| */ |
| - getToggleElement: function() { |
| - return this.querySelector( |
| - '.extension-error-list-show-more [is="action-link"]:not([hidden])'); |
| + clear: function() { |
| + if (this.errors_.length == 0) |
| + return; |
| + |
| + var ids = []; |
| + this.errors_.forEach(function(error) { ids.push(error.id); }); |
|
Dan Beam
2015/03/31 00:33:50
var ids = this.errors_.map(function(error) { retur
Devlin
2015/03/31 16:17:50
Done.
|
| + chrome.developerPrivate.deleteExtensionErrors({ |
| + extensionId: this.extensionId_, |
| + errorIds: ids |
| + }); |
| + |
| + this.setActiveErrorNode_(null); |
| + this.errors_.splice(0, this.errors_.length); |
|
Dan Beam
2015/03/31 00:33:49
this.errors_.length = 0;
Devlin
2015/03/31 16:17:51
How have I never seen this? Awesome.
|
| + var errorList = this.querySelector('.extension-error-list-contents'); |
| + while (errorList.firstChild) |
| + errorList.removeChild(errorList.firstChild); |
|
Dan Beam
2015/03/31 00:33:50
this.querySelector('.extension-error-list-contents
Devlin
2015/03/31 16:17:50
Pushing back on this. Two reasons:
- *** It seems
|
| }, |
| - /** @return {!Element} The element containing the list of errors. */ |
| - getErrorListElement: function() { |
| - return this.gridBoundary_; |
| + /** |
| + * Sets the active error in the list. |
| + * @param {?} node The error to make active. |
| + * @private |
| + */ |
| + setActiveErrorNode_: function(node) { |
| + if (this.activeError_) |
| + this.activeError_.classList.remove('extension-error-active'); |
|
Dan Beam
2015/03/31 00:33:49
nit: \n
Devlin
2015/03/31 16:17:50
Done.
|
| + if (node) |
| + node.classList.add('extension-error-active'); |
|
Dan Beam
2015/03/31 00:33:50
nit: \n
Devlin
2015/03/31 16:17:50
Done.
|
| + this.activeError_ = node; |
| + |
| + this.dispatchEvent( |
| + new CustomEvent('activeExtensionErrorChanged', |
| + { bubbles: true, detail: node ? node.error : null })); |
| }, |
| /** |
| @@ -188,65 +332,8 @@ cr.define('extensions', function() { |
| */ |
| 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_); |
| - |
| - 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]; |
| - } |
| - |
| activeRow.getEquivalentElement(null).focus(); |
| }, |
| - |
| - /** |
| - * Initialize the "Show More" link for the error list. If there are more |
| - * than |MAX_ERRORS_TO_SHOW_| errors in the list. |
| - * @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'); |
| - }); |
| - |
| - link.addEventListener('click', function(e) { |
| - // Needs to be enabled in case the focused row is now hidden. |
| - this.gridBoundary_.tabIndex = 0; |
| - |
| - link.isShowingAll = !link.isShowingAll; |
| - |
| - var message = link.isShowingAll ? 'extensionErrorsShowFewer' : |
| - 'extensionErrorsShowMore'; |
| - link.textContent = loadTimeData.getString(message); |
| - |
| - // Disable scrolling while transitioning. If the element is active, |
| - // scrolling is enabled when the transition ends. |
| - listContents.classList.remove('scrollable'); |
| - |
| - if (link.isShowingAll) { |
| - listContents.classList.add('active'); |
| - listContents.classList.remove('deactivating'); |
| - } else { |
| - listContents.classList.add('deactivating'); |
| - } |
| - }.bind(this)); |
| - } |
| }; |
| return { |