Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(170)

Side by Side Diff: chrome/browser/resources/extensions/extension_error.js

Issue 1016413004: [Extensions] Update Error Console UI (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Dan's Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 cr.define('extensions', function() { 5 cr.define('extensions', function() {
6 'use strict'; 6 'use strict';
7 7
8 /** 8 /**
9 * Clone a template within the extension error template collection. 9 * Clone a template within the extension error template collection.
10 * @param {string} templateName The class name of the template to clone. 10 * @param {string} templateName The class name of the template to clone.
(...skipping 28 matching lines...) Expand all
39 div.__proto__ = ExtensionError.prototype; 39 div.__proto__ = ExtensionError.prototype;
40 div.decorate(error, boundary); 40 div.decorate(error, boundary);
41 return div; 41 return div;
42 } 42 }
43 43
44 ExtensionError.prototype = { 44 ExtensionError.prototype = {
45 __proto__: cr.ui.FocusRow.prototype, 45 __proto__: cr.ui.FocusRow.prototype,
46 46
47 /** @override */ 47 /** @override */
48 getEquivalentElement: function(element) { 48 getEquivalentElement: function(element) {
49 return assert(this.querySelector('.extension-error-view-details')); 49 if (element.classList.contains('extension-error-metadata'))
50 return this;
51 if (element.classList.contains('error-delete-button')) {
52 return /** @type {!HTMLElement} */ (this.querySelector(
53 '.error-delete-button'));
54 }
55 assertNotReached();
56 return element;
50 }, 57 },
51 58
52 /** 59 /**
53 * @param {(RuntimeError|ManifestError)} error The error the element should 60 * @param {(RuntimeError|ManifestError)} error The error the element should
54 * represent 61 * represent
55 * @param {Element} boundary The boundary for the FocusGrid. 62 * @param {Element} boundary The boundary for the FocusGrid.
56 * @override 63 * @override
57 */ 64 */
58 decorate: function(error, boundary) { 65 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.
59 cr.ui.FocusRow.prototype.decorate.call(this, boundary); 66 cr.ui.FocusRow.prototype.decorate.call(this, boundary);
60 67
68 /**
69 * The backing error.
70 * @type {(ManifestError|RuntimeError)}
71 */
72 this.error = error;
73
61 // Add an additional class for the severity level. 74 // Add an additional class for the severity level.
62 if (error.type == chrome.developerPrivate.ErrorType.RUNTIME) { 75 if (error.type == chrome.developerPrivate.ErrorType.RUNTIME) {
63 switch (error.severity) { 76 switch (error.severity) {
64 case chrome.developerPrivate.ErrorLevel.LOG: 77 case chrome.developerPrivate.ErrorLevel.LOG:
65 this.classList.add('extension-error-severity-info'); 78 this.classList.add('extension-error-severity-info');
66 break; 79 break;
67 case chrome.developerPrivate.ErrorLevel.WARN: 80 case chrome.developerPrivate.ErrorLevel.WARN:
68 this.classList.add('extension-error-severity-warning'); 81 this.classList.add('extension-error-severity-warning');
69 break; 82 break;
70 case chrome.developerPrivate.ErrorLevel.ERROR: 83 case chrome.developerPrivate.ErrorLevel.ERROR:
71 this.classList.add('extension-error-severity-fatal'); 84 this.classList.add('extension-error-severity-fatal');
72 break; 85 break;
73 default: 86 default:
74 assertNotReached(); 87 assertNotReached();
75 } 88 }
76 } else { 89 } else {
77 // We classify manifest errors as "warnings". 90 // We classify manifest errors as "warnings".
78 this.classList.add('extension-error-severity-warning'); 91 this.classList.add('extension-error-severity-warning');
79 } 92 }
80 93
81 var iconNode = document.createElement('img'); 94 var iconNode = document.createElement('img');
82 iconNode.className = 'extension-error-icon'; 95 iconNode.className = 'extension-error-icon';
83 // TODO(hcarmona): Populate alt text with a proper description since this 96 // TODO(hcarmona): Populate alt text with a proper description since this
84 // icon conveys the severity of the error. (info, warning, fatal). 97 // icon conveys the severity of the error. (info, warning, fatal).
85 iconNode.alt = ''; 98 iconNode.alt = '';
86 this.insertBefore(iconNode, this.firstChild); 99 this.insertBefore(iconNode, this.firstChild);
87 100
88 var messageSpan = this.querySelector('.extension-error-message'); 101 var messageSpan = this.querySelector('.extension-error-message');
89 messageSpan.textContent = error.message; 102 messageSpan.textContent = error.message;
90 messageSpan.title = error.message;
91 103
92 var extensionUrl = 'chrome-extension://' + error.extensionId + '/'; 104 var deleteButton = this.querySelector('.error-delete-button');
93 var viewDetailsLink = this.querySelector('.extension-error-view-details'); 105 deleteButton.addEventListener('click', function(e) {
106 this.dispatchEvent(
107 new CustomEvent('deleteExtensionError',
108 {bubbles: true, detail: this.error}));
109 }.bind(this));
94 110
95 // If we cannot open the file source and there are no external frames in 111 this.addEventListener('click', function(e) {
96 // the stack, then there are no details to display. 112 if (e.target != deleteButton)
97 if (!extensions.ExtensionErrorOverlay.canShowOverlayForError( 113 this.requestActive_();
98 error, extensionUrl)) { 114 }.bind(this));
99 viewDetailsLink.hidden = true; 115 this.addEventListener('keydown', function(e) {
100 } else { 116 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.
101 var stringId = extensionUrl.toLowerCase() == 'manifest.json' ? 117 this.requestActive_();
102 'extensionErrorViewManifest' : 'extensionErrorViewDetails'; 118 });
103 viewDetailsLink.textContent = loadTimeData.getString(stringId);
104 119
105 viewDetailsLink.addEventListener('click', function(e) { 120 this.addFocusableElement(this);
106 extensions.ExtensionErrorOverlay.getInstance().setErrorAndShowOverlay( 121 this.addFocusableElement(this.querySelector('.error-delete-button'));
107 error, extensionUrl); 122 },
108 });
109 123
110 this.addFocusableElement(viewDetailsLink); 124 /**
111 } 125 * Bubble up an event to request to become active.
126 * @private
127 */
128 requestActive_: function() {
129 this.dispatchEvent(
130 new CustomEvent('highlightExtensionError',
131 {bubbles: true, detail: this.error}));
112 }, 132 },
113 }; 133 };
114 134
115 /** 135 /**
116 * A variable length list of runtime or manifest errors for a given extension. 136 * A variable length list of runtime or manifest errors for a given extension.
117 * @param {Array<(RuntimeError|ManifestError)>} errors The list of extension 137 * @param {Array<(RuntimeError|ManifestError)>} errors The list of extension
118 * errors with which to populate the list. 138 * errors with which to populate the list.
139 * @param {string} extensionId The id of the extension.
119 * @constructor 140 * @constructor
120 * @extends {HTMLDivElement} 141 * @extends {HTMLDivElement}
121 */ 142 */
122 function ExtensionErrorList(errors) { 143 function ExtensionErrorList(errors, extensionId) {
123 var div = cloneTemplate('extension-error-list'); 144 var div = cloneTemplate('extension-error-list');
124 div.__proto__ = ExtensionErrorList.prototype; 145 div.__proto__ = ExtensionErrorList.prototype;
125 div.errors_ = errors; 146 div.extensionId_ = extensionId;
126 div.decorate(); 147 div.decorate(errors);
127 return div; 148 return div;
128 } 149 }
129 150
130 /**
131 * @private
132 * @const
133 * @type {number}
134 */
135 ExtensionErrorList.MAX_ERRORS_TO_SHOW_ = 3;
136
137 ExtensionErrorList.prototype = { 151 ExtensionErrorList.prototype = {
138 __proto__: HTMLDivElement.prototype, 152 __proto__: HTMLDivElement.prototype,
139 153
140 decorate: function() { 154 /**
155 * Initializes the extension error list.
156 * @param {Array<(RuntimeError|ManifestError)>} errors The list of errors.
157 */
158 decorate: function(errors) {
159 /**
160 * @private {Array<(ManifestError|RuntimeError)>}
161 */
162 this.errors_ = [];
163
141 this.focusGrid_ = new cr.ui.FocusGrid(); 164 this.focusGrid_ = new cr.ui.FocusGrid();
142 this.gridBoundary_ = this.querySelector('.extension-error-list-contents'); 165 this.gridBoundary_ = this.querySelector('.extension-error-list-contents');
143 this.gridBoundary_.addEventListener('focus', this.onFocus_.bind(this)); 166 this.gridBoundary_.addEventListener('focus', this.onFocus_.bind(this));
144 this.gridBoundary_.addEventListener('focusin', 167 this.gridBoundary_.addEventListener('focusin',
145 this.onFocusin_.bind(this)); 168 this.onFocusin_.bind(this));
169 errors.forEach(this.addError_, this);
170
171 this.addEventListener('highlightExtensionError', function(e) {
172 this.setActiveErrorNode_(e.target);
173 });
174 this.addEventListener('deleteExtensionError', function(e) {
175 this.removeError_(e.detail);
176 });
177
178 this.querySelector('#extension-error-list-clear').addEventListener(
179 'click', function(e) {
180 this.clear();
181 }.bind(this));
182
183 chrome.developerPrivate.onItemStateChanged.addListener(
184 function(data) {
185 var type = chrome.developerPrivate.EventType;
186 if ((data.event_type == type.ERRORS_REMOVED ||
187 data.event_type == type.ERROR_ADDED) &&
188 data.info.id == this.extensionId_) {
189 var newErrors =
190 data.info.runtimeErrors.concat(data.info.manifestErrors);
191 this.updateErrors_(newErrors);
192 }
193 }.bind(this));
194
195 /**
196 * The active error element in the list.
197 * @private {?}
198 */
199 this.activeError_ = null;
200
201 this.setActiveError(0);
202 },
203
204 /**
205 * Adds an error to the list.
206 * @param {(RuntimeError|ManifestError)} error The error to add.
207 * @private
208 */
209 addError_: function(error) {
210 this.errors_.push(error);
211 var focusRow = new ExtensionError(error, this.gridBoundary_);
212 this.gridBoundary_.appendChild(document.createElement('li')).
213 appendChild(focusRow);
214 this.focusGrid_.addRow(focusRow);
215 },
216
217 /**
218 * Removes an error from the list.
219 * @param {(RuntimeError|ManifestError)} error The error to remove.
220 * @private
221 */
222 removeError_: function(error) {
223 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
224 var errorList = this.querySelector('.extension-error-list-contents');
225
226 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.
227
228 this.errors_.splice(index, 1);
229 var listElement = errorList.children[index];
230 listElement.parentNode.removeChild(listElement);
231
232 if (wasActive) {
233 index = Math.min(index, this.errors_.length - 1);
234 this.setActiveError(index); // Gracefully handles the -1 case.
235 }
236
237 chrome.developerPrivate.deleteExtensionErrors({
238 extensionId: error.extensionId,
239 errorIds: [error.id]
240 });
241 },
242
243 /**
244 * Updates the list of errors.
245 * @param {Array<(ManifestError|RuntimeError)>} newErrors The new list of
246 * errors.
247 * @private
248 */
249 updateErrors_: function(newErrors) {
250 var listHasError = function(list, id) {
251 for (var i = 0; i < list.length; ++i) {
252 if (list[i].id == id)
253 return true;
254 }
255 return false;
256 };
146 this.errors_.forEach(function(error) { 257 this.errors_.forEach(function(error) {
147 if (idIsValid(error.extensionId)) { 258 if (!listHasError(newErrors, error.id))
148 var focusRow = new ExtensionError(error, this.gridBoundary_); 259 this.removeError_(error);
149 this.gridBoundary_.appendChild(
150 document.createElement('li')).appendChild(focusRow);
151 this.focusGrid_.addRow(focusRow);
152 }
153 }, this); 260 }, this);
154 261 newErrors.forEach(function(error) {
155 var numShowing = this.focusGrid_.rows.length; 262 if (!listHasError(this.errors_, error.id))
156 if (numShowing > ExtensionErrorList.MAX_ERRORS_TO_SHOW_) 263 this.addError_(error);
157 this.initShowMoreLink_(); 264 }, this);
158 }, 265 },
159 266
160 /** 267 /**
161 * @return {?Element} The element that toggles between show more and show 268 * Sets the active error in the list.
162 * less, or null if it's hidden. Button will be hidden if there are less 269 * @param {number} index The index to set to be active.
163 * errors than |MAX_ERRORS_TO_SHOW_|. 270 */
164 */ 271 setActiveError: function(index) {
165 getToggleElement: function() { 272 var errorList = this.querySelector('.extension-error-list-contents');
166 return this.querySelector( 273 var node = null;
167 '.extension-error-list-show-more [is="action-link"]:not([hidden])'); 274 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
168 }, 275 node = errorList.children[index].querySelector(
169 276 '.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. :)
170 /** @return {!Element} The element containing the list of errors. */ 277 }
171 getErrorListElement: function() { 278 this.setActiveErrorNode_(node);
172 return this.gridBoundary_; 279 },
280
281 /**
282 * Clears the list of all errors.
283 */
284 clear: function() {
285 if (this.errors_.length == 0)
286 return;
287
288 var ids = this.errors_.map(function(error) { return error.id; });
289 chrome.developerPrivate.deleteExtensionErrors({
290 extensionId: this.extensionId_,
291 errorIds: ids
292 });
293
294 this.setActiveErrorNode_(null);
295 this.errors_.length = 0;
296 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
297 while (errorList.firstChild)
298 errorList.removeChild(errorList.firstChild);
299 },
300
301 /**
302 * Sets the active error in the list.
303 * @param {?} node The error to make active.
304 * @private
305 */
306 setActiveErrorNode_: function(node) {
307 if (this.activeError_)
308 this.activeError_.classList.remove('extension-error-active');
309
310 if (node)
311 node.classList.add('extension-error-active');
312
313 this.activeError_ = node;
314
315 this.dispatchEvent(
316 new CustomEvent('activeExtensionErrorChanged',
317 {bubbles: true, detail: node ? node.error : null}));
173 }, 318 },
174 319
175 /** 320 /**
176 * The grid should not be focusable once it or an element inside it is 321 * The grid should not be focusable once it or an element inside it is
177 * focused. This is necessary to allow tabbing out of the grid in reverse. 322 * focused. This is necessary to allow tabbing out of the grid in reverse.
178 * @private 323 * @private
179 */ 324 */
180 onFocusin_: function() { 325 onFocusin_: function() {
181 this.gridBoundary_.tabIndex = -1; 326 this.gridBoundary_.tabIndex = -1;
182 }, 327 },
183 328
184 /** 329 /**
185 * Focus the first focusable row when tabbing into the grid for the 330 * Focus the first focusable row when tabbing into the grid for the
186 * first time. 331 * first time.
187 * @private 332 * @private
188 */ 333 */
189 onFocus_: function() { 334 onFocus_: function() {
190 var activeRow = this.gridBoundary_.querySelector('.focus-row-active'); 335 var activeRow = this.gridBoundary_.querySelector('.focus-row-active');
191 var toggleButton = this.getToggleElement();
192
193 if (toggleButton && !toggleButton.isShowingAll) {
194 var rows = this.focusGrid_.rows;
195 assert(rows.length > ExtensionErrorList.MAX_ERRORS_TO_SHOW_);
196
197 var firstVisible = rows.length - ExtensionErrorList.MAX_ERRORS_TO_SHOW_;
198 if (rows.indexOf(activeRow) < firstVisible)
199 activeRow = rows[firstVisible];
200 } else if (!activeRow) {
201 activeRow = this.focusGrid_.rows[0];
202 }
203
204 activeRow.getEquivalentElement(null).focus(); 336 activeRow.getEquivalentElement(null).focus();
205 }, 337 },
206
207 /**
208 * Initialize the "Show More" link for the error list. If there are more
209 * than |MAX_ERRORS_TO_SHOW_| errors in the list.
210 * @private
211 */
212 initShowMoreLink_: function() {
213 var link = this.querySelector(
214 '.extension-error-list-show-more [is="action-link"]');
215 link.hidden = false;
216 link.isShowingAll = false;
217
218 var listContents = this.querySelector('.extension-error-list-contents');
219
220 // TODO(dbeam/kalman): trade all this transition voodoo for .animate()?
221 listContents.addEventListener('webkitTransitionEnd', function(e) {
222 if (listContents.classList.contains('deactivating'))
223 listContents.classList.remove('deactivating', 'active');
224 else
225 listContents.classList.add('scrollable');
226 });
227
228 link.addEventListener('click', function(e) {
229 // Needs to be enabled in case the focused row is now hidden.
230 this.gridBoundary_.tabIndex = 0;
231
232 link.isShowingAll = !link.isShowingAll;
233
234 var message = link.isShowingAll ? 'extensionErrorsShowFewer' :
235 'extensionErrorsShowMore';
236 link.textContent = loadTimeData.getString(message);
237
238 // Disable scrolling while transitioning. If the element is active,
239 // scrolling is enabled when the transition ends.
240 listContents.classList.remove('scrollable');
241
242 if (link.isShowingAll) {
243 listContents.classList.add('active');
244 listContents.classList.remove('deactivating');
245 } else {
246 listContents.classList.add('deactivating');
247 }
248 }.bind(this));
249 }
250 }; 338 };
251 339
252 return { 340 return {
253 ExtensionErrorList: ExtensionErrorList 341 ExtensionErrorList: ExtensionErrorList
254 }; 342 };
255 }); 343 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698