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

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

Issue 1025703003: [Extensions Page] Fix the flicker of "No extensions" before extension data loads (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 9 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
« no previous file with comments | « no previous file | chrome/browser/resources/extensions/extensions.css » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 // ExtensionFocusRow: 8 // ExtensionFocusRow:
9 9
10 /** 10 /**
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after
137 137
138 cr.define('extensions', function() { 138 cr.define('extensions', function() {
139 'use strict'; 139 'use strict';
140 140
141 /** 141 /**
142 * Creates a new list of extensions. 142 * Creates a new list of extensions.
143 * @param {Object=} opt_propertyBag Optional properties. 143 * @param {Object=} opt_propertyBag Optional properties.
144 * @constructor 144 * @constructor
145 * @extends {HTMLDivElement} 145 * @extends {HTMLDivElement}
146 */ 146 */
147 var ExtensionList = cr.ui.define('div'); 147 var ExtensionList = cr.ui.define('div');
Dan Beam 2015/03/21 01:44:20 if cr.ui.define() is limiting, do this instead fu
Devlin 2015/03/21 04:54:00 Done.
148 148
149 /** 149 /**
150 * @type {Object<string, number>} A map from extension id to last reloaded 150 * @type {Object<string, number>} A map from extension id to last reloaded
151 * timestamp. The timestamp is recorded when the user click the 'Reload' 151 * timestamp. The timestamp is recorded when the user click the 'Reload'
152 * link. It is used to refresh the icon of an unpacked extension. 152 * link. It is used to refresh the icon of an unpacked extension.
153 * This persists between calls to decorate. 153 * This persists between calls to decorate.
154 */ 154 */
155 var extensionReloadedTimestamp = {}; 155 var extensionReloadedTimestamp = {};
156 156
157 ExtensionList.prototype = { 157 ExtensionList.prototype = {
(...skipping 23 matching lines...) Expand all
181 * @private {boolean} 181 * @private {boolean}
182 */ 182 */
183 permissionsPromptIsShowing_: false, 183 permissionsPromptIsShowing_: false,
184 184
185 /** 185 /**
186 * Necessary to only show the butterbar once. 186 * Necessary to only show the butterbar once.
187 * @private {boolean} 187 * @private {boolean}
188 */ 188 */
189 butterbarShown_: false, 189 butterbarShown_: false,
190 190
191 /**
192 * Whether or not incognito mode is available.
193 * @private {boolean}
194 */
195 incognitoAvailable_: false,
196
197 /**
198 * Whether or not the app info dialog is enabled.
199 * @private {boolean}
200 */
201 enableAppInfoDialog_: false,
202
203 /** Needed because we use cr.ui.define. */
Dan Beam 2015/03/21 01:44:20 see above
Devlin 2015/03/21 04:54:00 Done.
191 decorate: function() { 204 decorate: function() {
192 chrome.developerPrivate.getExtensionsInfo( 205 /** @private {!Array<ExtensionInfo>} */
193 {includeDisabled: true, includeTerminated: true}, 206 this.extensions_ = [];
194 function(extensions) { 207 },
195 // Sort in order of unpacked vs. packed, followed by name, followed by 208
196 // id. 209 /**
197 extensions.sort(function(a, b) { 210 * Updates the extensions on the page.
198 function compare(x, y) { 211 * @param {boolean} incognitoAvailable Whether or not incognito is allowed.
199 return x < y ? -1 : (x > y ? 1 : 0); 212 * @param {boolean} enableAppInfoDialog Whether or not the app info dialog
200 } 213 * is enabled.
201 function compareLocation(x, y) { 214 * @return {Promise} A promise that is resolved once the extensions data is
202 return x.location == chrome.developerPrivate.Location.UNPACKED ? 215 * fully updated.
203 -1 : (x.location == y.location ? 0 : 1); 216 */
204 } 217 updateExtensionsData: function(incognitoAvailable, enableAppInfoDialog) {
205 return compareLocation(a, b) || 218 // If we start to need more information about the extension configuration,
206 compare(a.name.toLowerCase(), b.name.toLowerCase()) || 219 // consider passing in the full object from the ExtensionSettings.
207 compare(a.id, b.id); 220 this.incognitoAvailable_ = incognitoAvailable;
208 }); 221 this.enableAppInfoDialog_ = enableAppInfoDialog;
209 this.extensions_ = extensions; 222 return new Promise(function(resolve, reject) {
210 this.showExtensionNodes_(); 223 chrome.developerPrivate.getExtensionsInfo(
224 {includeDisabled: true, includeTerminated: true},
225 function(extensions) {
226 // Sort in order of unpacked vs. packed, followed by name, followed by
227 // id.
228 extensions.sort(function(a, b) {
229 function compare(x, y) {
230 return x < y ? -1 : (x > y ? 1 : 0);
231 }
232 function compareLocation(x, y) {
233 return x.location == chrome.developerPrivate.Location.UNPACKED ?
234 -1 : (x.location == y.location ? 0 : 1);
235 }
236 return compareLocation(a, b) ||
237 compare(a.name.toLowerCase(), b.name.toLowerCase()) ||
238 compare(a.id, b.id);
239 });
240 this.extensions_ = extensions;
241 this.showExtensionNodes_();
242 resolve();
243 }.bind(this));
211 }.bind(this)); 244 }.bind(this));
212 }, 245 },
213 246
247 /** @return {number} The number of extensions being displayed. */
248 getNumExtensions: function() {
249 return this.extensions_.length;
250 },
251
214 getIdQueryParam_: function() { 252 getIdQueryParam_: function() {
215 return parseQueryParams(document.location)['id']; 253 return parseQueryParams(document.location)['id'];
216 }, 254 },
217 255
218 getOptionsQueryParam_: function() { 256 getOptionsQueryParam_: function() {
219 return parseQueryParams(document.location)['options']; 257 return parseQueryParams(document.location)['options'];
220 }, 258 },
221 259
222 /** 260 /**
223 * Creates or updates all extension items from scratch. 261 * Creates or updates all extension items from scratch.
224 * @private 262 * @private
225 */ 263 */
226 showExtensionNodes_: function() { 264 showExtensionNodes_: function() {
227 // Remove the rows from |focusGrid_| without destroying them. 265 // Remove the rows from |focusGrid_| without destroying them.
228 this.focusGrid_.rows.length = 0; 266 this.focusGrid_.rows.length = 0;
229 267
230 // Any node that is not updated will be removed. 268 // Any node that is not updated will be removed.
231 var seenIds = []; 269 var seenIds = [];
232 270
233 // Iterate over the extension data and add each item to the list. 271 // Iterate over the extension data and add each item to the list.
234 this.extensions_.forEach(function(extension, i) { 272 this.extensions_.forEach(function(extension, i) {
235 var nextExt = this.extensions_[i + 1]; 273 var nextExt = this.extensions_[i + 1];
236 var node = $(extension.id); 274 var node = /** @type {ExtensionFocusRow} */ ($(extension.id));
237 seenIds.push(extension.id); 275 seenIds.push(extension.id);
238 276
239 if (node) 277 if (node)
240 this.updateNode_(extension, node); 278 this.updateNode_(extension, node);
241 else 279 else
242 this.createNode_(extension, nextExt ? $(nextExt.id) : null); 280 this.createNode_(extension, nextExt ? $(nextExt.id) : null);
243 }, this); 281 }, this);
244 282
245 // Remove extensions that are no longer installed. 283 // Remove extensions that are no longer installed.
246 var nodes = document.querySelectorAll('.extension-list-item-wrapper[id]'); 284 var nodes = document.querySelectorAll('.extension-list-item-wrapper[id]');
(...skipping 14 matching lines...) Expand all
261 } 299 }
262 } 300 }
263 301
264 var idToHighlight = this.getIdQueryParam_(); 302 var idToHighlight = this.getIdQueryParam_();
265 if (idToHighlight && $(idToHighlight)) 303 if (idToHighlight && $(idToHighlight))
266 this.scrollToNode_(idToHighlight); 304 this.scrollToNode_(idToHighlight);
267 305
268 var idToOpenOptions = this.getOptionsQueryParam_(); 306 var idToOpenOptions = this.getOptionsQueryParam_();
269 if (idToOpenOptions && $(idToOpenOptions)) 307 if (idToOpenOptions && $(idToOpenOptions))
270 this.showEmbeddedExtensionOptions_(idToOpenOptions, true); 308 this.showEmbeddedExtensionOptions_(idToOpenOptions, true);
271
272 var noExtensions = this.extensions_.length == 0;
273 this.classList.toggle('empty-extension-list', noExtensions);
274 }, 309 },
275 310
276 /** Updates each row's focusable elements without rebuilding the grid. */ 311 /** Updates each row's focusable elements without rebuilding the grid. */
277 updateFocusableElements: function() { 312 updateFocusableElements: function() {
278 var rows = document.querySelectorAll('.extension-list-item-wrapper[id]'); 313 var rows = document.querySelectorAll('.extension-list-item-wrapper[id]');
279 for (var i = 0; i < rows.length; ++i) { 314 for (var i = 0; i < rows.length; ++i) {
280 assertInstanceof(rows[i], ExtensionFocusRow).updateFocusableElements(); 315 assertInstanceof(rows[i], ExtensionFocusRow).updateFocusableElements();
281 } 316 }
282 }, 317 },
283 318
(...skipping 254 matching lines...) Expand 10 before | Expand all | Expand 10 after
538 this.setText_(row, '.location-text', extension.locationText || ''); 573 this.setText_(row, '.location-text', extension.locationText || '');
539 this.setText_(row, '.blacklist-text', extension.blacklistText || ''); 574 this.setText_(row, '.blacklist-text', extension.blacklistText || '');
540 this.setText_(row, '.extension-description', extension.description); 575 this.setText_(row, '.extension-description', extension.description);
541 576
542 // The 'Show Browser Action' button. 577 // The 'Show Browser Action' button.
543 this.updateVisibility_(row, '.show-button', 578 this.updateVisibility_(row, '.show-button',
544 isActive && extension.actionButtonHidden); 579 isActive && extension.actionButtonHidden);
545 580
546 // The 'allow in incognito' checkbox. 581 // The 'allow in incognito' checkbox.
547 this.updateVisibility_(row, '.incognito-control', 582 this.updateVisibility_(row, '.incognito-control',
548 isActive && this.data_.incognitoAvailable, 583 isActive && this.incognitoAvailable_,
549 function(item) { 584 function(item) {
550 var incognito = item.querySelector('input'); 585 var incognito = item.querySelector('input');
551 incognito.disabled = !extension.incognitoAccess.isEnabled; 586 incognito.disabled = !extension.incognitoAccess.isEnabled;
552 incognito.checked = extension.incognitoAccess.isActive; 587 incognito.checked = extension.incognitoAccess.isActive;
553 }); 588 });
554 589
555 // Hide butterBar if incognito is not enabled for the extension. 590 // Hide butterBar if incognito is not enabled for the extension.
556 var butterBar = row.querySelector('.butter-bar'); 591 var butterBar = row.querySelector('.butter-bar');
557 butterBar.hidden = 592 butterBar.hidden =
558 butterBar.hidden || !extension.incognitoAccess.isEnabled; 593 butterBar.hidden || !extension.incognitoAccess.isEnabled;
(...skipping 28 matching lines...) Expand all
587 622
588 // The 'Options' button or link, depending on its behaviour. 623 // The 'Options' button or link, depending on its behaviour.
589 var optionsEnabled = isActive && !!extension.optionsPage; 624 var optionsEnabled = isActive && !!extension.optionsPage;
590 this.updateVisibility_(row, '.options-link', optionsEnabled && 625 this.updateVisibility_(row, '.options-link', optionsEnabled &&
591 extension.optionsPage.openInTab); 626 extension.optionsPage.openInTab);
592 this.updateVisibility_(row, '.options-button', optionsEnabled && 627 this.updateVisibility_(row, '.options-button', optionsEnabled &&
593 !extension.optionsPage.openInTab); 628 !extension.optionsPage.openInTab);
594 629
595 // The 'View in Web Store/View Web Site' link. 630 // The 'View in Web Store/View Web Site' link.
596 var siteLinkEnabled = !!extension.homepageUrl && 631 var siteLinkEnabled = !!extension.homepageUrl &&
597 !this.data_.enableAppInfoDialog; 632 !this.enableAppInfoDialog_;
598 this.updateVisibility_(row, '.site-link', siteLinkEnabled, 633 this.updateVisibility_(row, '.site-link', siteLinkEnabled,
599 function(item) { 634 function(item) {
600 item.href = extension.homepageUrl; 635 item.href = extension.homepageUrl;
601 item.textContent = loadTimeData.getString( 636 item.textContent = loadTimeData.getString(
602 extension.homepageProvided ? 'extensionSettingsVisitWebsite' : 637 extension.homepageProvided ? 'extensionSettingsVisitWebsite' :
603 'extensionSettingsVisitWebStore'); 638 'extensionSettingsVisitWebStore');
604 }); 639 });
605 640
606 var isUnpacked = 641 var isUnpacked =
607 extension.location == chrome.developerPrivate.Location.UNPACKED; 642 extension.location == chrome.developerPrivate.Location.UNPACKED;
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after
714 } 749 }
715 if (!dependentExtension) 750 if (!dependentExtension)
716 return; 751 return;
717 752
718 var depNode = dependentTemplate.cloneNode(true); 753 var depNode = dependentTemplate.cloneNode(true);
719 depNode.querySelector('.dep-extension-title').textContent = 754 depNode.querySelector('.dep-extension-title').textContent =
720 dependentExtension.name; 755 dependentExtension.name;
721 depNode.querySelector('.dep-extension-id').textContent = 756 depNode.querySelector('.dep-extension-id').textContent =
722 dependentExtension.id; 757 dependentExtension.id;
723 dependentList.appendChild(depNode); 758 dependentList.appendChild(depNode);
724 }); 759 }, this);
725 }); 760 });
726 761
727 // The active views. 762 // The active views.
728 this.updateVisibility_(row, '.active-views', extension.views.length > 0, 763 this.updateVisibility_(row, '.active-views', extension.views.length > 0,
729 function(item) { 764 function(item) {
730 var link = item.querySelector('a'); 765 var link = item.querySelector('a');
731 766
732 // Link needs to be an only child before the list is updated. 767 // Link needs to be an only child before the list is updated.
733 while (link.nextElementSibling) 768 while (link.nextElementSibling)
734 item.removeChild(link.nextElementSibling); 769 item.removeChild(link.nextElementSibling);
(...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after
940 // TODO(dbeam): why do we need to focus <extensionoptions> before and 975 // TODO(dbeam): why do we need to focus <extensionoptions> before and
941 // after its showing animation? Makes very little sense to me. 976 // after its showing animation? Makes very little sense to me.
942 overlay.setInitialFocus(); 977 overlay.setInitialFocus();
943 }, 978 },
944 }; 979 };
945 980
946 return { 981 return {
947 ExtensionList: ExtensionList 982 ExtensionList: ExtensionList
948 }; 983 };
949 }); 984 });
OLDNEW
« no previous file with comments | « no previous file | chrome/browser/resources/extensions/extensions.css » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698