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 { |