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

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

Issue 916243002: Enable keyboard shortcuts for chrome://extensions (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix closure compile 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 (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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 <include src="extension_error.js"> 5 <include src="extension_error.js">
6 6
7 /** 7 /**
8 * The type of the extension data object. The definition is based on 8 * The type of the extension data object. The definition is based on
9 * chrome/browser/ui/webui/extensions/extension_basic_info.cc 9 * chrome/browser/ui/webui/extensions/extension_basic_info.cc
10 * and 10 * and
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
58 * version: string, 58 * version: string,
59 * views: Array<{renderViewId: number, renderProcessId: number, 59 * views: Array<{renderViewId: number, renderProcessId: number,
60 * path: string, incognito: boolean, 60 * path: string, incognito: boolean,
61 * generatedBackgroundPage: boolean}>, 61 * generatedBackgroundPage: boolean}>,
62 * wantsErrorCollection: boolean, 62 * wantsErrorCollection: boolean,
63 * wantsFileAccess: boolean, 63 * wantsFileAccess: boolean,
64 * warnings: (Array|undefined)}} 64 * warnings: (Array|undefined)}}
65 */ 65 */
66 var ExtensionData; 66 var ExtensionData;
67 67
68 ///////////////////////////////////////////////////////////////////////////////
69 // ExtensionFocusRow:
70
71 /**
72 * Provides an implementation for a single column grid.
73 * @constructor
74 * @extends {cr.ui.FocusRow}
75 */
76 function ExtensionFocusRow() {}
77
78 /**
79 * Decorates |focusRow| so that it can be treated as a ExtensionFocusRow.
80 * @param {Element} focusRow The element that has all the columns.
81 * @param {Node} boundary Focus events are ignored outside of this node.
82 */
83 ExtensionFocusRow.decorate = function(focusRow, boundary) {
84 focusRow.__proto__ = ExtensionFocusRow.prototype;
85 focusRow.decorate(boundary);
86 };
87
88 ExtensionFocusRow.prototype = {
89 __proto__: cr.ui.FocusRow.prototype,
90
91 /** @override */
92 getEquivalentElement: function(element) {
93 if (this.focusableElements.indexOf(element) > -1)
94 return element;
95
96 // All elements default to another element with the same type.
97 var columnType = element.getAttribute('column-type');
98 var equivalent = this.querySelector('[column-type=' + columnType + ']');
99
100 if (!equivalent || !this.canAddElement_(equivalent)) {
101 var actionLinks = ['options', 'website', 'launch', 'localReload'];
102 if (actionLinks.indexOf(columnType) > -1)
103 equivalent = this.getFirstFocusableByType_(actionLinks);
Dan Beam 2015/02/19 19:26:28 why are we potentially overwriting |equivalent| 4
hcarmona 2015/02/19 23:42:17 Only one should match. Changed to else if.
104
105 var optionalControls = ['showButton', 'incognito', 'dev-collectErrors',
106 'allUrls', 'localUrls'];
107 if (optionalControls.indexOf(columnType) > -1)
108 equivalent = this.getFirstFocusableByType_(optionalControls);
109
110 var removeStyleButtons = ['trash', 'enterprise'];
111 if (removeStyleButtons.indexOf(columnType) > -1)
112 equivalent = this.getFirstFocusableByType_(removeStyleButtons);
113
114 var enableControls = ['terminatedReload', 'repair', 'enabled'];
115 if (enableControls.indexOf(columnType) > -1)
116 equivalent = this.getFirstFocusableByType_(enableControls);
117 }
118
119 // Return the first focusable element if no equivalent type is found.
120 return equivalent || this.focusableElements[0];
121 },
122
123 /** Updates the list of focusable elements. */
124 updateFocusableElements: function() {
125 this.focusableElements.length = 0;
126
127 var focusableCandidates = this.querySelectorAll('[column-type]');
128 for (var i = 0; i < focusableCandidates.length; ++i) {
129 var element = focusableCandidates[i];
130 if (this.canAddElement_(element))
131 this.addFocusableElement(element);
132 }
133 },
134
135 /**
136 * Get the first focusable element that matches a list of types.
137 * @param {Array<string>} types An array of types to match from.
138 * @return {?Element} Return the first element that matches a type in |types|.
139 * @private
140 */
141 getFirstFocusableByType_: function(types) {
142 for (var i = 0; i < this.focusableElements.length; ++i) {
143 var element = this.focusableElements[i];
144 if (types.indexOf(element.getAttribute('column-type')) > -1)
145 return element;
146 }
147 return null;
148 },
149
150 /**
151 * @param {Element} element
152 * @return {boolean}
153 * @private
154 */
155 canAddElement_: function(element) {
156 if (!element || element.disabled)
157 return false;
158
159 var developerMode = $('extension-settings').classList.contains('dev-mode');
160 if (this.isDeveloperOption_(element) && !developerMode)
161 return false;
162
163 for (var el = element; el; el = el.parentElement) {
164 if (el.hidden)
165 return false;
166 }
167
168 return true;
169 },
170
171 /**
172 * Returns true if the element should only be shown in developer mode.
173 * @param {Element} element
174 * @return {boolean}
175 * @private
176 */
177 isDeveloperOption_: function(element) {
178 var type = element.getAttribute('column-type');
179 return type.indexOf('dev-') == 0;
Dan Beam 2015/02/19 19:26:28 this can fit in one line as: return element.get
hcarmona 2015/02/19 23:42:17 Done.
180 },
181 };
182
68 cr.define('options', function() { 183 cr.define('options', function() {
69 'use strict'; 184 'use strict';
70 185
71 /** 186 /**
72 * Creates a new list of extensions. 187 * Creates a new list of extensions.
73 * @param {Object=} opt_propertyBag Optional properties. 188 * @param {Object=} opt_propertyBag Optional properties.
74 * @constructor 189 * @constructor
75 * @extends {HTMLDivElement} 190 * @extends {HTMLDivElement}
76 */ 191 */
77 var ExtensionsList = cr.ui.define('div'); 192 var ExtensionsList = cr.ui.define('div');
(...skipping 11 matching lines...) Expand all
89 204
90 /** 205 /**
91 * Indicates whether an embedded options page that was navigated to through 206 * Indicates whether an embedded options page that was navigated to through
92 * the '?options=' URL query has been shown to the user. This is necessary 207 * the '?options=' URL query has been shown to the user. This is necessary
93 * to prevent showExtensionNodes_ from opening the options more than once. 208 * to prevent showExtensionNodes_ from opening the options more than once.
94 * @type {boolean} 209 * @type {boolean}
95 * @private 210 * @private
96 */ 211 */
97 optionsShown_: false, 212 optionsShown_: false,
98 213
214 /** @private {cr.ui.FocusGrid} */
Dan Beam 2015/02/19 19:26:28 nit: !cr.ui.FocusGrid
hcarmona 2015/02/19 23:42:17 Done.
215 focusGrid_: new cr.ui.FocusGrid(),
216
99 /** 217 /**
100 * Necessary to only show the butterbar once. 218 * Necessary to only show the butterbar once.
101 * @private {boolean} 219 * @private {boolean}
102 */ 220 */
103 butterbarShown_: false, 221 butterbarShown_: false,
104 222
105 decorate: function() { 223 decorate: function() {
106 this.showExtensionNodes_(); 224 this.showExtensionNodes_();
107 }, 225 },
108 226
109 getIdQueryParam_: function() { 227 getIdQueryParam_: function() {
110 return parseQueryParams(document.location)['id']; 228 return parseQueryParams(document.location)['id'];
111 }, 229 },
112 230
113 getOptionsQueryParam_: function() { 231 getOptionsQueryParam_: function() {
114 return parseQueryParams(document.location)['options']; 232 return parseQueryParams(document.location)['options'];
115 }, 233 },
116 234
117 /** 235 /**
118 * Creates all extension items from scratch. 236 * Creates or updates all extension items from scratch.
119 * @private 237 * @private
120 */ 238 */
121 showExtensionNodes_: function() { 239 showExtensionNodes_: function() {
240 // Remove the rows from |focusGrid_| without destroying them.
241 this.focusGrid_.rows.length = 0;
242
122 // Any node that is not updated will be removed. 243 // Any node that is not updated will be removed.
123 var seenIds = []; 244 var seenIds = [];
124 245
125 // Iterate over the extension data and add each item to the list. 246 // Iterate over the extension data and add each item to the list.
126 this.data_.extensions.forEach(function(extension) { 247 this.data_.extensions.forEach(function(extension) {
127 var node = $(extension.id); 248 var node = $(extension.id);
128 seenIds.push(extension.id); 249 seenIds.push(extension.id);
129 250
130 if (node) 251 if (node)
131 this.updateNode_(extension, node); 252 this.updateNode_(extension, node);
132 else 253 else
133 this.createNode_(extension); 254 this.createNode_(extension);
134 }, this); 255 }, this);
135 256
136 // Remove extensions that are no longer installed. 257 // Remove extensions that are no longer installed or add them to
258 // |focusGrid_| if they are still installed.
137 var nodes = document.querySelectorAll('.extension-list-item-wrapper[id]'); 259 var nodes = document.querySelectorAll('.extension-list-item-wrapper[id]');
138 for (var i = 0; i < nodes.length; ++i) { 260 for (var i = 0; i < nodes.length; ++i) {
139 var node = nodes[i]; 261 var node = nodes[i];
140 if (seenIds.indexOf(node.id) < 0) 262 if (seenIds.indexOf(node.id) < 0) {
141 node.parentElement.removeChild(node); 263 node.parentElement.removeChild(node);
264 // Unregister the removed node from events.
265 node.destroy();
266 } else {
267 this.focusGrid_.addRow(node);
268 }
142 } 269 }
143 270
144 var idToHighlight = this.getIdQueryParam_(); 271 var idToHighlight = this.getIdQueryParam_();
145 if (idToHighlight && $(idToHighlight)) 272 if (idToHighlight && $(idToHighlight))
146 this.scrollToNode_(idToHighlight); 273 this.scrollToNode_(idToHighlight);
147 274
148 var idToOpenOptions = this.getOptionsQueryParam_(); 275 var idToOpenOptions = this.getOptionsQueryParam_();
149 if (idToOpenOptions && $(idToOpenOptions)) 276 if (idToOpenOptions && $(idToOpenOptions))
150 this.showEmbeddedExtensionOptions_(idToOpenOptions, true); 277 this.showEmbeddedExtensionOptions_(idToOpenOptions, true);
151 278
152 var noExtensions = this.data_.extensions.length == 0; 279 var noExtensions = this.data_.extensions.length == 0;
153 this.classList.toggle('empty-extension-list', noExtensions); 280 this.classList.toggle('empty-extension-list', noExtensions);
154 }, 281 },
155 282
283 /** Updates each row's focusable elements without rebuilding the grid. */
284 updateFocusableElements: function() {
285 var nodes = document.querySelectorAll('.extension-list-item-wrapper[id]');
Dan Beam 2015/02/19 19:26:28 nit: nodes -> rows
hcarmona 2015/02/19 23:42:17 Done.
286 for (var i = 0; i < nodes.length; ++i) {
287 nodes[i].updateFocusableElements();
288 }
289 },
290
156 /** 291 /**
157 * Scrolls the page down to the extension node with the given id. 292 * Scrolls the page down to the extension node with the given id.
158 * @param {string} extensionId The id of the extension to scroll to. 293 * @param {string} extensionId The id of the extension to scroll to.
159 * @private 294 * @private
160 */ 295 */
161 scrollToNode_: function(extensionId) { 296 scrollToNode_: function(extensionId) {
162 // Scroll offset should be calculated slightly higher than the actual 297 // Scroll offset should be calculated slightly higher than the actual
163 // offset of the element being scrolled to, so that it ends up not all 298 // offset of the element being scrolled to, so that it ends up not all
164 // the way at the top. That way it is clear that there are more elements 299 // the way at the top. That way it is clear that there are more elements
165 // above the element being scrolled to. 300 // above the element being scrolled to.
166 var scrollFudge = 1.2; 301 var scrollFudge = 1.2;
167 var scrollTop = $(extensionId).offsetTop - scrollFudge * 302 var scrollTop = $(extensionId).offsetTop - scrollFudge *
168 $(extensionId).clientHeight; 303 $(extensionId).clientHeight;
169 setScrollTopForDocument(document, scrollTop); 304 setScrollTopForDocument(document, scrollTop);
170 }, 305 },
171 306
172 /** 307 /**
173 * Synthesizes and initializes an HTML element for the extension metadata 308 * Synthesizes and initializes an HTML element for the extension metadata
174 * given in |extension|. 309 * given in |extension|.
175 * @param {ExtensionData} extension A dictionary of extension metadata. 310 * @param {ExtensionData} extension A dictionary of extension metadata.
176 * @private 311 * @private
177 */ 312 */
178 createNode_: function(extension) { 313 createNode_: function(extension) {
179 var template = $('template-collection').querySelector( 314 var template = $('template-collection').querySelector(
180 '.extension-list-item-wrapper'); 315 '.extension-list-item-wrapper');
181 var node = template.cloneNode(true); 316 var node = template.cloneNode(true);
317 ExtensionFocusRow.decorate(node, $('extension-settings-list'));
182 node.id = extension.id; 318 node.id = extension.id;
183 319
184 // The 'Show Browser Action' button. 320 // The 'Show Browser Action' button.
185 this.addListener_('click', node, '.show-button', function(e) { 321 this.addListener_('click', node, '.show-button', 'showButton',
322 function(e) {
186 chrome.send('extensionSettingsShowButton', [extension.id]); 323 chrome.send('extensionSettingsShowButton', [extension.id]);
187 }); 324 });
188 325
189 // The 'allow in incognito' checkbox. 326 // The 'allow in incognito' checkbox.
190 this.addListener_('change', node, '.incognito-control input', 327 this.addListener_('change', node, '.incognito-control input', 'incognito',
191 function(e) { 328 function(e) {
192 var butterBar = node.querySelector('.butter-bar'); 329 var butterBar = node.querySelector('.butter-bar');
193 var checked = e.target.checked; 330 var checked = e.target.checked;
194 if (!this.butterbarShown_) { 331 if (!this.butterbarShown_) {
195 butterBar.hidden = !checked || extension.is_hosted_app; 332 butterBar.hidden = !checked || extension.is_hosted_app;
196 this.butterbarShown_ = !butterBar.hidden; 333 this.butterbarShown_ = !butterBar.hidden;
197 } else 334 } else
198 butterBar.hidden = true; 335 butterBar.hidden = true;
199 chrome.send('extensionSettingsEnableIncognito', 336 chrome.send('extensionSettingsEnableIncognito',
200 [extension.id, String(checked)]); 337 [extension.id, String(checked)]);
201 }.bind(this)); 338 }.bind(this));
202 339
203 // The 'collect errors' checkbox. This should only be visible if the 340 // The 'collect errors' checkbox. This should only be visible if the
204 // error console is enabled - we can detect this by the existence of the 341 // error console is enabled - we can detect this by the existence of the
205 // |errorCollectionEnabled| property. 342 // |errorCollectionEnabled| property.
206 this.addListener_('change', node, '.error-collection-control input', 343 this.addListener_('change', node, '.error-collection-control input',
207 function(e) { 344 'dev-collectErrors', function(e) {
208 chrome.send('extensionSettingsEnableErrorCollection', 345 chrome.send('extensionSettingsEnableErrorCollection',
209 [extension.id, String(e.target.checked)]); 346 [extension.id, String(e.target.checked)]);
210 }); 347 });
211 348
212 // The 'allow on all urls' checkbox. This should only be visible if 349 // The 'allow on all urls' checkbox. This should only be visible if
213 // active script restrictions are enabled. If they are not enabled, no 350 // active script restrictions are enabled. If they are not enabled, no
214 // extensions should want all urls. 351 // extensions should want all urls.
215 this.addListener_('click', node, '.all-urls-control', function(e) { 352 this.addListener_('click', node, '.all-urls-control input', 'allUrls',
353 function(e) {
216 chrome.send('extensionSettingsAllowOnAllUrls', 354 chrome.send('extensionSettingsAllowOnAllUrls',
217 [extension.id, String(e.target.checked)]); 355 [extension.id, String(e.target.checked)]);
218 }); 356 });
219 357
220 // The 'allow file:// access' checkbox. 358 // The 'allow file:// access' checkbox.
221 this.addListener_('click', node, '.file-access-control', function(e) { 359 this.addListener_('click', node, '.file-access-control input',
360 'localUrls', function(e) {
222 chrome.send('extensionSettingsAllowFileAccess', 361 chrome.send('extensionSettingsAllowFileAccess',
223 [extension.id, String(e.target.checked)]); 362 [extension.id, String(e.target.checked)]);
224 }); 363 });
225 364
226 // The 'Options' button or link, depending on its behaviour. 365 // The 'Options' button or link, depending on its behaviour.
227 // Set an href to get the correct mouse-over appearance (link, 366 // Set an href to get the correct mouse-over appearance (link,
228 // footer) - but the actual link opening is done through chrome.send 367 // footer) - but the actual link opening is done through chrome.send
229 // with a preventDefault(). 368 // with a preventDefault().
230 node.querySelector('.options-link').href = extension.optionsPageHref; 369 node.querySelector('.options-link').href = extension.optionsPageHref;
231 this.addListener_('click', node, '.options-link', function(e) { 370 this.addListener_('click', node, '.options-link', 'options', function(e) {
232 chrome.send('extensionSettingsOptions', [extension.id]); 371 chrome.send('extensionSettingsOptions', [extension.id]);
233 e.preventDefault(); 372 e.preventDefault();
234 }); 373 });
235 374
236 this.addListener_('click', node, '.options-button', function(e) { 375 this.addListener_('click', node, '.options-button', 'options',
376 function(e) {
237 this.showEmbeddedExtensionOptions_(extension.id, false); 377 this.showEmbeddedExtensionOptions_(extension.id, false);
238 e.preventDefault(); 378 e.preventDefault();
239 }.bind(this)); 379 }.bind(this));
240 380
381 // The 'View in Web Store/View Web Site' link.
382 node.querySelector('.site-link').setAttribute('column-type', 'website');
383
241 // The 'Permissions' link. 384 // The 'Permissions' link.
242 this.addListener_('click', node, '.permissions-link', function(e) { 385 this.addListener_('click', node, '.permissions-link', 'details',
386 function(e) {
243 chrome.send('extensionSettingsPermissions', [extension.id]); 387 chrome.send('extensionSettingsPermissions', [extension.id]);
244 e.preventDefault(); 388 e.preventDefault();
245 }); 389 });
246 390
247 // The 'Reload' link. 391 // The 'Reload' link.
248 this.addListener_('click', node, '.reload-link', function(e) { 392 this.addListener_('click', node, '.reload-link', 'localReload',
393 function(e) {
249 chrome.send('extensionSettingsReload', [extension.id]); 394 chrome.send('extensionSettingsReload', [extension.id]);
250 extensionReloadedTimestamp[extension.id] = Date.now(); 395 extensionReloadedTimestamp[extension.id] = Date.now();
251 }); 396 });
252 397
253 // The 'Launch' link. 398 // The 'Launch' link.
254 this.addListener_('click', node, '.launch-link', function(e) { 399 this.addListener_('click', node, '.launch-link', 'launch', function(e) {
255 chrome.send('extensionSettingsLaunch', [extension.id]); 400 chrome.send('extensionSettingsLaunch', [extension.id]);
256 }); 401 });
257 402
258 // The 'Reload' terminated link. 403 // The 'Reload' terminated link.
259 this.addListener_('click', node, '.terminated-reload-link', function(e) { 404 this.addListener_('click', node, '.terminated-reload-link',
405 'terminatedReload', function(e) {
260 chrome.send('extensionSettingsReload', [extension.id]); 406 chrome.send('extensionSettingsReload', [extension.id]);
261 }); 407 });
262 408
263 // The 'Repair' corrupted link. 409 // The 'Repair' corrupted link.
264 this.addListener_('click', node, '.corrupted-repair-button', function(e) { 410 this.addListener_('click', node, '.corrupted-repair-button', 'repair',
411 function(e) {
265 chrome.send('extensionSettingsRepair', [extension.id]); 412 chrome.send('extensionSettingsRepair', [extension.id]);
266 }); 413 });
267 414
268 // The 'Enabled' checkbox. 415 // The 'Enabled' checkbox.
269 this.addListener_('change', node, '.enable-checkbox input', function(e) { 416 this.addListener_('change', node, '.enable-checkbox input', 'enabled',
417 function(e) {
270 var checked = e.target.checked; 418 var checked = e.target.checked;
271 chrome.send('extensionSettingsEnable', [extension.id, String(checked)]); 419 chrome.send('extensionSettingsEnable', [extension.id, String(checked)]);
272 420
273 // This may seem counter-intuitive (to not set/clear the checkmark) 421 // This may seem counter-intuitive (to not set/clear the checkmark)
274 // but this page will be updated asynchronously if the extension 422 // but this page will be updated asynchronously if the extension
275 // becomes enabled/disabled. It also might not become enabled or 423 // becomes enabled/disabled. It also might not become enabled or
276 // disabled, because the user might e.g. get prompted when enabling 424 // disabled, because the user might e.g. get prompted when enabling
277 // and choose not to. 425 // and choose not to.
278 e.preventDefault(); 426 e.preventDefault();
279 }); 427 });
280 428
281 // 'Remove' button. 429 // 'Remove' button.
282 var trashTemplate = $('template-collection').querySelector('.trash'); 430 var trashTemplate = $('template-collection').querySelector('.trash');
283 var trash = trashTemplate.cloneNode(true); 431 var trash = trashTemplate.cloneNode(true);
284 trash.title = loadTimeData.getString('extensionUninstall'); 432 trash.title = loadTimeData.getString('extensionUninstall');
433 trash.hidden = extension.managedInstall;
434 trash.setAttribute('column-type', 'trash');
285 trash.addEventListener('click', function(e) { 435 trash.addEventListener('click', function(e) {
286 chrome.send('extensionSettingsUninstall', [extension.id]); 436 chrome.send('extensionSettingsUninstall', [extension.id]);
287 }); 437 });
288 node.querySelector('.enable-controls').appendChild(trash); 438 node.querySelector('.enable-controls').appendChild(trash);
289 439
290 // Developer mode //////////////////////////////////////////////////////// 440 // Developer mode ////////////////////////////////////////////////////////
291 441
292 // The path, if provided by unpacked extension. 442 // The path, if provided by unpacked extension.
293 this.addListener_('click', node, '.load-path a:first-of-type', 443 this.addListener_('click', node, '.load-path a:first-of-type',
294 function(e) { 444 'dev-loadPath', function(e) {
295 chrome.send('extensionSettingsShowPath', [String(extension.id)]); 445 chrome.send('extensionSettingsShowPath', [String(extension.id)]);
296 e.preventDefault(); 446 e.preventDefault();
297 }); 447 });
298 448
299 this.appendChild(node); 449 this.appendChild(node);
300 this.updateNode_(extension, node); 450 this.updateNode_(extension, node);
301 }, 451 },
302 452
303 /** 453 /**
304 * Updates an HTML element for the extension metadata given in |extension|. 454 * Updates an HTML element for the extension metadata given in |extension|.
305 * @param {ExtensionData} extension A dictionary of extension metadata. 455 * @param {ExtensionData} extension A dictionary of extension metadata.
306 * @param {Element} node The node that is being updated. 456 * @param {Element} node The node that is being updated.
307 * @private 457 * @private
308 */ 458 */
309 updateNode_: function(extension, node) { 459 updateNode_: function(extension, node) {
310 node.classList.toggle('inactive-extension', 460 var isActive = extension.enabled && !extension.terminated;
311 !extension.enabled || extension.terminated); 461
462 node.classList.toggle('inactive-extension', !isActive);
312 463
313 node.classList.remove('policy-controlled', 'may-not-modify', 464 node.classList.remove('policy-controlled', 'may-not-modify',
314 'may-not-remove'); 465 'may-not-remove');
315 var classes = []; 466 var classes = [];
316 if (extension.managedInstall) { 467 if (extension.managedInstall) {
317 classes.push('policy-controlled', 'may-not-modify'); 468 classes.push('policy-controlled', 'may-not-modify');
318 } else if (extension.dependentExtensions.length > 0) { 469 } else if (extension.dependentExtensions.length > 0) {
319 classes.push('may-not-remove', 'may-not-modify'); 470 classes.push('may-not-remove', 'may-not-modify');
320 } else if (extension.recommendedInstall) { 471 } else if (extension.recommendedInstall) {
321 classes.push('may-not-remove'); 472 classes.push('may-not-remove');
(...skipping 21 matching lines...) Expand all
343 494
344 this.setText_(node, '.extension-title', extension.name); 495 this.setText_(node, '.extension-title', extension.name);
345 this.setText_(node, '.extension-version', extension.version); 496 this.setText_(node, '.extension-version', extension.version);
346 this.setText_(node, '.location-text', extension.locationText); 497 this.setText_(node, '.location-text', extension.locationText);
347 this.setText_(node, '.blacklist-text', extension.blacklistText); 498 this.setText_(node, '.blacklist-text', extension.blacklistText);
348 this.setText_(node, '.extension-description', 499 this.setText_(node, '.extension-description',
349 extension.description); 500 extension.description);
350 501
351 // The 'Show Browser Action' button. 502 // The 'Show Browser Action' button.
352 this.updateVisibility_(node, '.show-button', 503 this.updateVisibility_(node, '.show-button',
353 extension.enable_show_button); 504 isActive && extension.enable_show_button);
354 505
355 // The 'allow in incognito' checkbox. 506 // The 'allow in incognito' checkbox.
356 this.updateVisibility_(node, '.incognito-control', 507 this.updateVisibility_(node, '.incognito-control',
357 this.data_.incognitoAvailable, function(item) { 508 isActive && this.data_.incognitoAvailable,
509 function(item) {
358 var incognito = item.querySelector('input'); 510 var incognito = item.querySelector('input');
359 incognito.disabled = !extension.incognitoCanBeEnabled; 511 incognito.disabled = !extension.incognitoCanBeEnabled;
360 incognito.checked = extension.enabledIncognito; 512 incognito.checked = extension.enabledIncognito;
361 }); 513 });
362 514
363 // Hide butterBar if incognito is not enabled for the extension. 515 // Hide butterBar if incognito is not enabled for the extension.
364 var butterBar = node.querySelector('.butter-bar'); 516 var butterBar = node.querySelector('.butter-bar');
365 butterBar.hidden = butterBar.hidden || !extension.enabledIncognito; 517 butterBar.hidden = butterBar.hidden || !extension.enabledIncognito;
366 518
367 // The 'collect errors' checkbox. This should only be visible if the 519 // The 'collect errors' checkbox. This should only be visible if the
368 // error console is enabled - we can detect this by the existence of the 520 // error console is enabled - we can detect this by the existence of the
369 // |errorCollectionEnabled| property. 521 // |errorCollectionEnabled| property.
370 this.updateVisibility_(node, '.error-collection-control', 522 this.updateVisibility_(node, '.error-collection-control',
371 extension.wantsErrorCollection, function(item) { 523 isActive && extension.wantsErrorCollection,
524 function(item) {
372 item.querySelector('input').checked = extension.errorCollectionEnabled; 525 item.querySelector('input').checked = extension.errorCollectionEnabled;
373 }); 526 });
374 527
375 // The 'allow on all urls' checkbox. This should only be visible if 528 // The 'allow on all urls' checkbox. This should only be visible if
376 // active script restrictions are enabled. If they are not enabled, no 529 // active script restrictions are enabled. If they are not enabled, no
377 // extensions should want all urls. 530 // extensions should want all urls.
378 this.updateVisibility_(node, '.all-urls-control', extension.showAllUrls, 531 this.updateVisibility_(node, '.all-urls-control',
379 function(item) { 532 isActive && extension.showAllUrls, function(item) {
380 item.querySelector('input').checked = extension.allowAllUrls; 533 item.querySelector('input').checked = extension.allowAllUrls;
381 }); 534 });
382 535
383 // The 'allow file:// access' checkbox. 536 // The 'allow file:// access' checkbox.
384 this.updateVisibility_(node, '.file-access-control', 537 this.updateVisibility_(node, '.file-access-control',
385 extension.wantsFileAccess, function(item) { 538 isActive && extension.wantsFileAccess,
539 function(item) {
386 item.querySelector('input').checked = extension.allowFileAccess; 540 item.querySelector('input').checked = extension.allowFileAccess;
387 }); 541 });
388 542
389 // The 'Options' button or link, depending on its behaviour. 543 // The 'Options' button or link, depending on its behaviour.
390 var optionsEnabled = extension.enabled && !!extension.optionsUrl; 544 var optionsEnabled = extension.enabled && !!extension.optionsUrl;
391 this.updateVisibility_(node, '.options-link', optionsEnabled && 545 this.updateVisibility_(node, '.options-link', optionsEnabled &&
392 extension.optionsOpenInTab); 546 extension.optionsOpenInTab);
393 this.updateVisibility_(node, '.options-button', optionsEnabled && 547 this.updateVisibility_(node, '.options-button', optionsEnabled &&
394 !extension.optionsOpenInTab); 548 !extension.optionsOpenInTab);
395 549
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
439 var needsIndicator = isOK && extension.managedInstall; 593 var needsIndicator = isOK && extension.managedInstall;
440 594
441 if (needsIndicator && !indicator) { 595 if (needsIndicator && !indicator) {
442 indicator = new cr.ui.ControlledIndicator(); 596 indicator = new cr.ui.ControlledIndicator();
443 indicator.classList.add('controlled-extension-indicator'); 597 indicator.classList.add('controlled-extension-indicator');
444 indicator.setAttribute('controlled-by', 'policy'); 598 indicator.setAttribute('controlled-by', 'policy');
445 var textPolicy = extension.policyText || ''; 599 var textPolicy = extension.policyText || '';
446 indicator.setAttribute('textpolicy', textPolicy); 600 indicator.setAttribute('textpolicy', textPolicy);
447 indicator.image.setAttribute('aria-label', textPolicy); 601 indicator.image.setAttribute('aria-label', textPolicy);
448 controlNode.appendChild(indicator); 602 controlNode.appendChild(indicator);
603 indicator.querySelector('div').setAttribute('column-type',
604 'enterprise');
449 } else if (!needsIndicator && indicator) { 605 } else if (!needsIndicator && indicator) {
450 controlNode.removeChild(indicator); 606 controlNode.removeChild(indicator);
451 } 607 }
452 608
453 // Developer mode //////////////////////////////////////////////////////// 609 // Developer mode ////////////////////////////////////////////////////////
454 610
455 // First we have the id. 611 // First we have the id.
456 var idLabel = node.querySelector('.extension-id'); 612 var idLabel = node.querySelector('.extension-id');
457 idLabel.textContent = ' ' + extension.id; 613 idLabel.textContent = ' ' + extension.id;
458 614
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
533 view.incognito 689 view.incognito
534 ]); 690 ]);
535 }; 691 };
536 link.addEventListener('click', link.clickHandler); 692 link.addEventListener('click', link.clickHandler);
537 693
538 if (i < extension.views.length - 1) { 694 if (i < extension.views.length - 1) {
539 link = link.cloneNode(true); 695 link = link.cloneNode(true);
540 item.appendChild(link); 696 item.appendChild(link);
541 } 697 }
542 }); 698 });
699
700 var allLinks = item.querySelectorAll('a');
701 for (var i = 0; i < allLinks.length; ++i) {
702 allLinks[i].setAttribute('column-type', 'dev-activeViews' + i);
703 }
543 }); 704 });
544 705
545 // The extension warnings (describing runtime issues). 706 // The extension warnings (describing runtime issues).
546 this.updateVisibility_(node, '.extension-warnings', !!extension.warnings, 707 this.updateVisibility_(node, '.extension-warnings', !!extension.warnings,
547 function(item) { 708 function(item) {
548 var warningList = item.querySelector('ul'); 709 var warningList = item.querySelector('ul');
549 warningList.textContent = ''; 710 warningList.textContent = '';
550 extension.warnings.forEach(function(warning) { 711 extension.warnings.forEach(function(warning) {
551 var li = document.createElement('li'); 712 var li = document.createElement('li');
552 warningList.appendChild(li).innerText = warning; 713 warningList.appendChild(li).innerText = warning;
553 }); 714 });
554 }); 715 });
555 716
556 // If the ErrorConsole is enabled, we should have manifest and/or runtime 717 // If the ErrorConsole is enabled, we should have manifest and/or runtime
557 // errors. Otherwise, we may have install warnings. We should not have 718 // errors. Otherwise, we may have install warnings. We should not have
558 // both ErrorConsole errors and install warnings. 719 // both ErrorConsole errors and install warnings.
559 // Errors. 720 // Errors.
560 this.updateErrors_(node.querySelector('.manifest-errors'), 721 this.updateErrors_(node.querySelector('.manifest-errors'),
561 extension.manifestErrors); 722 'dev-manifestErrors', extension.manifestErrors);
562 this.updateErrors_(node.querySelector('.runtime-errors'), 723 this.updateErrors_(node.querySelector('.runtime-errors'),
563 extension.runtimeErrors); 724 'dev-runtimeErrors', extension.runtimeErrors);
564 725
565 // Install warnings. 726 // Install warnings.
566 this.updateVisibility_(node, '.install-warnings', 727 this.updateVisibility_(node, '.install-warnings',
567 !!extension.installWarnings, function(item) { 728 !!extension.installWarnings, function(item) {
568 var installWarningList = item.querySelector('ul'); 729 var installWarningList = item.querySelector('ul');
569 installWarningList.textContent = ''; 730 installWarningList.textContent = '';
570 if (extension.installWarnings) { 731 if (extension.installWarnings) {
571 extension.installWarnings.forEach(function(warning) { 732 extension.installWarnings.forEach(function(warning) {
572 var li = document.createElement('li'); 733 var li = document.createElement('li');
573 li.innerText = warning.message; 734 li.innerText = warning.message;
574 installWarningList.appendChild(li); 735 installWarningList.appendChild(li);
575 }); 736 });
576 } 737 }
577 }); 738 });
578 739
579 if (location.hash.substr(1) == extension.id) { 740 if (location.hash.substr(1) == extension.id) {
580 // Scroll beneath the fixed header so that the extension is not 741 // Scroll beneath the fixed header so that the extension is not
581 // obscured. 742 // obscured.
582 var topScroll = node.offsetTop - $('page-header').offsetHeight; 743 var topScroll = node.offsetTop - $('page-header').offsetHeight;
583 var pad = parseInt(window.getComputedStyle(node, null).marginTop, 10); 744 var pad = parseInt(window.getComputedStyle(node, null).marginTop, 10);
584 if (!isNaN(pad)) 745 if (!isNaN(pad))
585 topScroll -= pad / 2; 746 topScroll -= pad / 2;
586 setScrollTopForDocument(document, topScroll); 747 setScrollTopForDocument(document, topScroll);
587 } 748 }
749
750 node.updateFocusableElements();
Dan Beam 2015/02/19 19:26:28 every time I see node#focusRowFunction() my eyes a
hcarmona 2015/02/19 23:42:17 Done.
588 }, 751 },
589 752
590 /** 753 /**
591 * Adds a listener to an element. 754 * Adds a listener to an element.
592 * @param {string} type The type of listener to add. 755 * @param {string} type The type of listener to add.
593 * @param {Element} node Ancestor of the element specified by |query|. 756 * @param {Element} node Ancestor of the element specified by |query|.
594 * @param {string} query A query to select an element in |node|. 757 * @param {string} query A query to select an element in |node|.
758 * @param {string} columnType A tag used to identify the column when
759 * changing focus.
595 * @param {function(Event)} handler The function that should be called 760 * @param {function(Event)} handler The function that should be called
596 * on click. 761 * on click.
597 * @private 762 * @private
598 */ 763 */
599 addListener_: function(type, node, query, handler) { 764 addListener_: function(type, node, query, columnType, handler) {
600 node.querySelector(query).addEventListener(type, handler); 765 var element = node.querySelector(query);
766 element.addEventListener(type, handler);
767 element.setAttribute('column-type', columnType);
Dan Beam 2015/02/19 19:26:28 this is an unexpected side effect in "addListener_
hcarmona 2015/02/19 23:42:17 Done.
601 }, 768 },
602 769
603 /** 770 /**
604 * Updates an element's textContent. 771 * Updates an element's textContent.
605 * @param {Element} node Ancestor of the element specified by |query|. 772 * @param {Element} node Ancestor of the element specified by |query|.
606 * @param {string} query A query to select an element in |node|. 773 * @param {string} query A query to select an element in |node|.
607 * @param {string} textContent 774 * @param {string} textContent
608 * @private 775 * @private
609 */ 776 */
610 setText_: function(node, query, textContent) { 777 setText_: function(node, query, textContent) {
(...skipping 14 matching lines...) Expand all
625 updateVisibility_: function(node, query, visible, opt_shownCallback) { 792 updateVisibility_: function(node, query, visible, opt_shownCallback) {
626 var item = assert(node.querySelector(query)); 793 var item = assert(node.querySelector(query));
627 item.hidden = !visible; 794 item.hidden = !visible;
628 if (visible && opt_shownCallback) 795 if (visible && opt_shownCallback)
629 opt_shownCallback(item); 796 opt_shownCallback(item);
630 }, 797 },
631 798
632 /** 799 /**
633 * Updates an element to show a list of errors. 800 * Updates an element to show a list of errors.
634 * @param {Element} panel An element to hold the errors. 801 * @param {Element} panel An element to hold the errors.
802 * @param {string} columnType A tag used to identify the column when
803 * changing focus.
635 * @param {Array<RuntimeError>|undefined} errors The errors to be displayed. 804 * @param {Array<RuntimeError>|undefined} errors The errors to be displayed.
636 * @private 805 * @private
637 */ 806 */
638 updateErrors_: function(panel, errors) { 807 updateErrors_: function(panel, columnType, errors) {
639 // TODO(hcarmona): Look into updating the ExtensionErrorList rather than 808 // TODO(hcarmona): Look into updating the ExtensionErrorList rather than
640 // rebuilding it every time. 809 // rebuilding it every time.
641 panel.hidden = !errors || errors.length == 0; 810 panel.hidden = !errors || errors.length == 0;
642 panel.textContent = ''; 811 panel.textContent = '';
643 if (!panel.hidden) { 812 if (!panel.hidden) {
644 panel.appendChild( 813 var errorList =
645 new extensions.ExtensionErrorList(assertInstanceof(errors, Array))); 814 new extensions.ExtensionErrorList(assertInstanceof(errors, Array));
815
816 panel.appendChild(errorList);
817
818 var list = errorList.getListElement();
819 if (list)
820 list.setAttribute('column-type', columnType + 'list');
821
822 var button = errorList.getToggleElement();
823 if (button)
824 button.setAttribute('column-type', columnType + 'button');
646 } 825 }
647 }, 826 },
648 827
649 /** 828 /**
650 * Opens the extension options overlay for the extension with the given id. 829 * Opens the extension options overlay for the extension with the given id.
651 * @param {string} extensionId The id of extension whose options page should 830 * @param {string} extensionId The id of extension whose options page should
652 * be displayed. 831 * be displayed.
653 * @param {boolean} scroll Whether the page should scroll to the extension 832 * @param {boolean} scroll Whether the page should scroll to the extension
654 * @private 833 * @private
655 */ 834 */
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
693 // TODO(dbeam): why do we need to focus <extensionoptions> before and 872 // TODO(dbeam): why do we need to focus <extensionoptions> before and
694 // after its showing animation? Makes very little sense to me. 873 // after its showing animation? Makes very little sense to me.
695 overlay.setInitialFocus(); 874 overlay.setInitialFocus();
696 }, 875 },
697 }; 876 };
698 877
699 return { 878 return {
700 ExtensionsList: ExtensionsList 879 ExtensionsList: ExtensionsList
701 }; 880 };
702 }); 881 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698