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

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: Applied Feedback 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);
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 = '';
not at google - send to devlin 2015/02/21 00:47:01 +rdevlin.cronin It looks like this alt should des
Dan Beam 2015/02/21 00:49:58 doesn't the message indicate severity in some way
not at google - send to devlin 2015/02/21 00:54:41 It's meant to emulate devtools. If I were to do:
Dan Beam 2015/02/21 00:55:53 so that's a "yes" :)
not at google - send to devlin 2015/02/21 00:57:05 Confused. But, I think my comment has merit.
Devlin 2015/02/23 16:36:28 Yeah, for perfect a11y this should describe the se
hcarmona 2015/02/23 22:25:08 Do we have 18n strings for these warnings? If so,
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)) {
not at google - send to devlin 2015/02/21 00:47:01 +rdevlin.cronin How can this idIsValid check fail
Devlin 2015/02/23 16:36:28 IIRC, this is just us being paranoid. Yes, these
not at google - send to devlin 2015/02/23 16:53:26 That's true. In this case I'd argue that it'd just
hcarmona 2015/02/23 22:25:08 What should we do about the check? Keep it or get
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
145 /** @return {?Element} The "Show more" element or null if it's hidden. */
146 getToggleElement: function() {
not at google - send to devlin 2015/02/21 00:47:01 The comment is more useful than the name of the fu
hcarmona 2015/02/23 22:25:08 Updated to comment to reflect that the "Show more"
147 return this.querySelector(
148 '.extension-error-list-show-more [is="action-link"]:not([hidden])');
149 },
150
151 /** @return {!Element} The element containing the list of errors. */
152 getListElement: function() {
not at google - send to devlin 2015/02/21 00:47:01 ListElement makes me think like HTMLUListElement o
hcarmona 2015/02/23 22:25:08 Done.
153 return this.gridBoundary_;
154 },
155
156 /**
157 * The gird should not be focusable once it or an element inside it is
not at google - send to devlin 2015/02/21 00:47:01 grid
Dan Beam 2015/02/21 00:49:58 #noregerts
hcarmona 2015/02/23 22:25:08 Done.
158 * focused. This is necessary to allow tabbing out of the grid in reverse.
159 * @private
160 */
161 onFocusin_: function() {
162 this.gridBoundary_.tabIndex = -1;
163 },
164
165 /**
166 * Focus the first focusable row when tabbing into the grid for the
167 * first time.
168 * @private
169 */
170 onFocus_: function() {
171 var activeRow = this.gridBoundary_.querySelector('.focus-row-active');
172 var toggleButton = this.getToggleElement();
173
174 if (!toggleButton || toggleButton.isShowingAll) {
175 activeRow = activeRow || this.focusGrid_.rows[0];
176 activeRow.getEquivalentElement(null).focus();
177 } else {
178 var rows = this.focusGrid_.rows;
179
180 var firstVisible = rows.length - ExtensionErrorList.MAX_ERRORS_TO_SHOW_;
181 var focusedIndex = rows.indexOf(activeRow);
182
183 if (focusedIndex < firstVisible)
184 activeRow = rows[firstVisible];
185
186 activeRow.getEquivalentElement(null).focus();
187 }
188 },
189
125 /** 190 /**
126 * Initialize the "Show More" link for the error list. If there are more 191 * Initialize the "Show More" link for the error list. If there are more
127 * than |MAX_ERRORS_TO_SHOW_| errors in the list. 192 * than |MAX_ERRORS_TO_SHOW_| errors in the list.
128 * @private 193 * @private
129 */ 194 */
130 initShowMoreLink_: function() { 195 initShowMoreLink_: function() {
131 var link = this.querySelector( 196 var link = this.querySelector(
132 '.extension-error-list-show-more [is="action-link"]'); 197 '.extension-error-list-show-more [is="action-link"]');
133 link.hidden = false; 198 link.hidden = false;
134 link.isShowingAll = false; 199 link.isShowingAll = false;
135 200
136 var listContents = this.querySelector('.extension-error-list-contents'); 201 var listContents = this.querySelector('.extension-error-list-contents');
137 202
138 // TODO(dbeam/kalman): trade all this transition voodoo for .animate()? 203 // TODO(dbeam/kalman): trade all this transition voodoo for .animate()?
139 listContents.addEventListener('webkitTransitionEnd', function(e) { 204 listContents.addEventListener('webkitTransitionEnd', function(e) {
140 if (listContents.classList.contains('deactivating')) 205 if (listContents.classList.contains('deactivating'))
141 listContents.classList.remove('deactivating', 'active'); 206 listContents.classList.remove('deactivating', 'active');
142 else 207 else
143 listContents.classList.add('scrollable'); 208 listContents.classList.add('scrollable');
144 }); 209 });
145 210
146 link.addEventListener('click', function(e) { 211 link.addEventListener('click', function(e) {
212 // Needs to be enabled in case the focused row is now hidden.
213 this.gridBoundary_.tabIndex = 0;
214
147 link.isShowingAll = !link.isShowingAll; 215 link.isShowingAll = !link.isShowingAll;
148 216
149 var message = link.isShowingAll ? 'extensionErrorsShowFewer' : 217 var message = link.isShowingAll ? 'extensionErrorsShowFewer' :
150 'extensionErrorsShowMore'; 218 'extensionErrorsShowMore';
151 link.textContent = loadTimeData.getString(message); 219 link.textContent = loadTimeData.getString(message);
152 220
153 // Disable scrolling while transitioning. If the element is active, 221 // Disable scrolling while transitioning. If the element is active,
154 // scrolling is enabled when the transition ends. 222 // scrolling is enabled when the transition ends.
155 listContents.classList.remove('scrollable'); 223 listContents.classList.remove('scrollable');
156 224
157 if (link.isShowingAll) { 225 if (link.isShowingAll) {
158 listContents.classList.add('active'); 226 listContents.classList.add('active');
159 listContents.classList.remove('deactivating'); 227 listContents.classList.remove('deactivating');
160 } else { 228 } else {
161 listContents.classList.add('deactivating'); 229 listContents.classList.add('deactivating');
162 } 230 }
163 }.bind(this)); 231 }.bind(this));
164 } 232 }
165 }; 233 };
166 234
167 return { 235 return {
168 ExtensionErrorList: ExtensionErrorList 236 ExtensionErrorList: ExtensionErrorList
169 }; 237 };
170 }); 238 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698