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

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