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

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

Issue 916243002: Enable keyboard shortcuts for chrome://extensions (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Attempt to fix trybots Created 5 years, 10 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 10 matching lines...) Expand all
21 * @param {string} id The Extension ID to test. 21 * @param {string} id The Extension ID to test.
22 * @return {boolean} Whether or not the ID is valid. 22 * @return {boolean} Whether or not the ID is valid.
23 */ 23 */
24 function idIsValid(id) { 24 function idIsValid(id) {
25 return /^[a-p]{32}$/.test(id); 25 return /^[a-p]{32}$/.test(id);
26 } 26 }
27 27
28 /** 28 /**
29 * Creates a new ExtensionError HTMLElement; this is used to show a 29 * Creates a new ExtensionError HTMLElement; this is used to show a
30 * notification to the user when an error is caused by an extension. 30 * notification to the user when an error is caused by an extension.
31 * @param {Object} error The error the element should represent. 31 * @param {RuntimeError} error The error the element should represent.
32 * @param {Element} boundary The boundary for the focus grid.
32 * @constructor 33 * @constructor
33 * @extends {HTMLDivElement} 34 * @extends {cr.ui.FocusRow}
34 */ 35 */
35 function ExtensionError(error) { 36 function ExtensionError(error, boundary) {
36 var div = cloneTemplate('extension-error-metadata'); 37 var div = cloneTemplate('extension-error-metadata');
37 div.__proto__ = ExtensionError.prototype; 38 div.__proto__ = ExtensionError.prototype;
38 div.decorate(error); 39 div.decorate(error, boundary);
39 return div; 40 return div;
40 } 41 }
41 42
42 ExtensionError.prototype = { 43 ExtensionError.prototype = {
43 __proto__: HTMLDivElement.prototype, 44 __proto__: cr.ui.FocusRow.prototype,
45
46 /** @override */
47 getEquivalentElement: function(element) {
48 return assertInstanceof(
49 this.querySelector('.extension-error-view-details'), Element);
Dan Beam 2015/02/24 00:55:37 nit: querySelector only returns Element|null, so j
hcarmona 2015/02/24 02:49:44 Done.
50 },
44 51
45 /** 52 /**
46 * @param {RuntimeError} error 53 * @param {RuntimeError} error The error the element should represent
54 * @param {Element} boundary The boundary for the FocusGrid.
55 * @override
47 */ 56 */
48 decorate: function(error) { 57 decorate: function(error, boundary) {
58 cr.ui.FocusRow.prototype.decorate.call(this, boundary);
59
49 // Add an additional class for the severity level. 60 // Add an additional class for the severity level.
50 if (error.level == 0) 61 if (error.level == 0)
51 this.classList.add('extension-error-severity-info'); 62 this.classList.add('extension-error-severity-info');
52 else if (error.level == 1) 63 else if (error.level == 1)
53 this.classList.add('extension-error-severity-warning'); 64 this.classList.add('extension-error-severity-warning');
54 else 65 else
55 this.classList.add('extension-error-severity-fatal'); 66 this.classList.add('extension-error-severity-fatal');
56 67
57 var iconNode = document.createElement('img'); 68 var iconNode = document.createElement('img');
58 iconNode.className = 'extension-error-icon'; 69 iconNode.className = 'extension-error-icon';
70 iconNode.alt = '';
Dan Beam 2015/02/24 00:55:37 nit: add TODO to fix
hcarmona 2015/02/24 02:49:44 Done.
59 this.insertBefore(iconNode, this.firstChild); 71 this.insertBefore(iconNode, this.firstChild);
60 72
61 var messageSpan = this.querySelector('.extension-error-message'); 73 var messageSpan = this.querySelector('.extension-error-message');
62 messageSpan.textContent = error.message; 74 messageSpan.textContent = error.message;
63 messageSpan.title = error.message; 75 messageSpan.title = error.message;
64 76
65 var extensionUrl = 'chrome-extension://' + error.extensionId + '/'; 77 var extensionUrl = 'chrome-extension://' + error.extensionId + '/';
66 var viewDetailsLink = this.querySelector('.extension-error-view-details'); 78 var viewDetailsLink = this.querySelector('.extension-error-view-details');
67 79
68 // If we cannot open the file source and there are no external frames in 80 // If we cannot open the file source and there are no external frames in
69 // the stack, then there are no details to display. 81 // the stack, then there are no details to display.
70 if (!extensions.ExtensionErrorOverlay.canShowOverlayForError( 82 if (!extensions.ExtensionErrorOverlay.canShowOverlayForError(
71 error, extensionUrl)) { 83 error, extensionUrl)) {
72 viewDetailsLink.hidden = true; 84 viewDetailsLink.hidden = true;
73 } else { 85 } else {
74 var stringId = extensionUrl.toLowerCase() == 'manifest.json' ? 86 var stringId = extensionUrl.toLowerCase() == 'manifest.json' ?
75 'extensionErrorViewManifest' : 'extensionErrorViewDetails'; 87 'extensionErrorViewManifest' : 'extensionErrorViewDetails';
76 viewDetailsLink.textContent = loadTimeData.getString(stringId); 88 viewDetailsLink.textContent = loadTimeData.getString(stringId);
77 89
78 viewDetailsLink.addEventListener('click', function(e) { 90 viewDetailsLink.addEventListener('click', function(e) {
79 extensions.ExtensionErrorOverlay.getInstance().setErrorAndShowOverlay( 91 extensions.ExtensionErrorOverlay.getInstance().setErrorAndShowOverlay(
80 error, extensionUrl); 92 error, extensionUrl);
81 }); 93 });
94
95 this.addFocusableElement(viewDetailsLink);
82 } 96 }
83 }, 97 },
84 }; 98 };
85 99
86 /** 100 /**
87 * A variable length list of runtime or manifest errors for a given extension. 101 * A variable length list of runtime or manifest errors for a given extension.
88 * @param {Array<Object>} errors The list of extension errors with which 102 * @param {Array<Object>} errors The list of extension errors with which
89 * to populate the list. 103 * to populate the list.
90 * @constructor 104 * @constructor
91 * @extends {HTMLDivElement} 105 * @extends {HTMLDivElement}
(...skipping 10 matching lines...) Expand all
102 * @private 116 * @private
103 * @const 117 * @const
104 * @type {number} 118 * @type {number}
105 */ 119 */
106 ExtensionErrorList.MAX_ERRORS_TO_SHOW_ = 3; 120 ExtensionErrorList.MAX_ERRORS_TO_SHOW_ = 3;
107 121
108 ExtensionErrorList.prototype = { 122 ExtensionErrorList.prototype = {
109 __proto__: HTMLDivElement.prototype, 123 __proto__: HTMLDivElement.prototype,
110 124
111 decorate: function() { 125 decorate: function() {
112 this.contents_ = this.querySelector('.extension-error-list-contents'); 126 this.focusGrid_ = new cr.ui.FocusGrid();
127 this.gridBoundary_ = this.querySelector('.extension-error-list-contents');
128 this.gridBoundary_.addEventListener('focus', this.onFocus_.bind(this));
129 this.gridBoundary_.addEventListener('focusin',
130 this.onFocusin_.bind(this));
113 this.errors_.forEach(function(error) { 131 this.errors_.forEach(function(error) {
114 if (idIsValid(error.extensionId)) { 132 if (idIsValid(error.extensionId)) {
115 this.contents_.appendChild(document.createElement('li')).appendChild( 133 var focusRow = new ExtensionError(error, this.gridBoundary_);
116 new ExtensionError(error)); 134 this.gridBoundary_.appendChild(
135 document.createElement('li')).appendChild(focusRow);
136 this.focusGrid_.addRow(focusRow);
117 } 137 }
118 }, this); 138 }, this);
119 139
120 var numShowing = this.contents_.children.length; 140 var numShowing = this.focusGrid_.rows.length;
121 if (numShowing > ExtensionErrorList.MAX_ERRORS_TO_SHOW_) 141 if (numShowing > ExtensionErrorList.MAX_ERRORS_TO_SHOW_)
122 this.initShowMoreLink_(); 142 this.initShowMoreLink_();
123 }, 143 },
124 144
125 /** 145 /**
146 * @return {?Element} The element that toggles between show more and show
147 * less, or null if it's hidden. Button will be hidden if there are less
148 * errors than |MAX_ERRORS_TO_SHOW_|.
149 */
150 getToggleElement: function() {
151 return this.querySelector(
152 '.extension-error-list-show-more [is="action-link"]:not([hidden])');
153 },
154
155 /** @return {!Element} The element containing the list of errors. */
156 getErrorListElement: function() {
157 return this.gridBoundary_;
158 },
159
160 /**
161 * The grid should not be focusable once it or an element inside it is
162 * focused. This is necessary to allow tabbing out of the grid in reverse.
163 * @private
164 */
165 onFocusin_: function() {
166 this.gridBoundary_.tabIndex = -1;
167 },
168
169 /**
170 * Focus the first focusable row when tabbing into the grid for the
171 * first time.
172 * @private
173 */
174 onFocus_: function() {
175 var activeRow = this.gridBoundary_.querySelector('.focus-row-active');
176 var toggleButton = this.getToggleElement();
177
178 if (!toggleButton || toggleButton.isShowingAll) {
Dan Beam 2015/02/24 00:55:37 i think this if is easier to read reversed as i
hcarmona 2015/02/24 02:49:44 I agree with this change. Done.
179 activeRow = activeRow || this.focusGrid_.rows[0];
180 activeRow.getEquivalentElement(null).focus();
181 } else {
182 var rows = this.focusGrid_.rows;
183
184 var firstVisible = rows.length - ExtensionErrorList.MAX_ERRORS_TO_SHOW_;
Dan Beam 2015/02/24 00:55:37 what if rows.length == 0 or less than MAX_ERRORS_T
hcarmona 2015/02/24 02:49:43 toggleButton == null if rows.length < MAX_ERRORS_T
Dan Beam 2015/02/24 18:16:46 i assume you mean <=
hcarmona 2015/02/24 18:41:09 Yes
185 var focusedIndex = rows.indexOf(activeRow);
186
187 if (focusedIndex < firstVisible)
188 activeRow = rows[firstVisible];
189
190 activeRow.getEquivalentElement(null).focus();
191 }
Dan Beam 2015/02/24 00:55:37 activeRow.getEquivalentElement(null).focus(); is
hcarmona 2015/02/24 02:49:44 Done.
192 },
193
194 /**
126 * Initialize the "Show More" link for the error list. If there are more 195 * Initialize the "Show More" link for the error list. If there are more
127 * than |MAX_ERRORS_TO_SHOW_| errors in the list. 196 * than |MAX_ERRORS_TO_SHOW_| errors in the list.
128 * @private 197 * @private
129 */ 198 */
130 initShowMoreLink_: function() { 199 initShowMoreLink_: function() {
131 var link = this.querySelector( 200 var link = this.querySelector(
132 '.extension-error-list-show-more [is="action-link"]'); 201 '.extension-error-list-show-more [is="action-link"]');
133 link.hidden = false; 202 link.hidden = false;
134 link.isShowingAll = false; 203 link.isShowingAll = false;
135 204
136 var listContents = this.querySelector('.extension-error-list-contents'); 205 var listContents = this.querySelector('.extension-error-list-contents');
137 206
138 // TODO(dbeam/kalman): trade all this transition voodoo for .animate()? 207 // TODO(dbeam/kalman): trade all this transition voodoo for .animate()?
139 listContents.addEventListener('webkitTransitionEnd', function(e) { 208 listContents.addEventListener('webkitTransitionEnd', function(e) {
140 if (listContents.classList.contains('deactivating')) 209 if (listContents.classList.contains('deactivating'))
141 listContents.classList.remove('deactivating', 'active'); 210 listContents.classList.remove('deactivating', 'active');
142 else 211 else
143 listContents.classList.add('scrollable'); 212 listContents.classList.add('scrollable');
144 }); 213 });
145 214
146 link.addEventListener('click', function(e) { 215 link.addEventListener('click', function(e) {
216 // Needs to be enabled in case the focused row is now hidden.
217 this.gridBoundary_.tabIndex = 0;
218
147 link.isShowingAll = !link.isShowingAll; 219 link.isShowingAll = !link.isShowingAll;
148 220
149 var message = link.isShowingAll ? 'extensionErrorsShowFewer' : 221 var message = link.isShowingAll ? 'extensionErrorsShowFewer' :
150 'extensionErrorsShowMore'; 222 'extensionErrorsShowMore';
151 link.textContent = loadTimeData.getString(message); 223 link.textContent = loadTimeData.getString(message);
152 224
153 // Disable scrolling while transitioning. If the element is active, 225 // Disable scrolling while transitioning. If the element is active,
154 // scrolling is enabled when the transition ends. 226 // scrolling is enabled when the transition ends.
155 listContents.classList.remove('scrollable'); 227 listContents.classList.remove('scrollable');
156 228
157 if (link.isShowingAll) { 229 if (link.isShowingAll) {
158 listContents.classList.add('active'); 230 listContents.classList.add('active');
159 listContents.classList.remove('deactivating'); 231 listContents.classList.remove('deactivating');
160 } else { 232 } else {
161 listContents.classList.add('deactivating'); 233 listContents.classList.add('deactivating');
162 } 234 }
163 }.bind(this)); 235 }.bind(this));
164 } 236 }
165 }; 237 };
166 238
167 return { 239 return {
168 ExtensionErrorList: ExtensionErrorList 240 ExtensionErrorList: ExtensionErrorList
169 }; 241 };
170 }); 242 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698